First version, for githup; UNSTABLE, DO NOT USE!

This commit is contained in:
Fabio Herzig
2026-04-12 21:25:44 +02:00
commit a51fd9dbeb
423 changed files with 58560 additions and 0 deletions

9
www/intern/.htaccess Normal file
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]

View File

@@ -0,0 +1,79 @@
.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);
}

View File

@@ -0,0 +1,91 @@
.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;
}
.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 {
font-size: 1rem;
}

View File

@@ -0,0 +1,175 @@
: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
}
/* 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 input {
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 input: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);
}
.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 one or more lines are too long

View File

@@ -0,0 +1,958 @@
:root {
--paddingSite: 40px;
--card-radius: 20px;
--card-bg: #ffffff;
--bg: #FDFDFD;
--card-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
--accent: #2d73ac;
--accent-soft: #f0f5ff;
--border-subtle: #d4d7e1;
--text-main: #191919;
--text-muted: #5e5e5e;
--disabled-bg: #f3f4f6;
--disabled-border: #cbd5f5;
}
/* Base */
*,
*::after,
*::before {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
font-family: "Google Sans Flex", sans-serif;
font-optical-sizing: auto;
font-style: normal;
font-variation-settings:
"slnt" 0,
"wdth" 100,
"GRAD" 0,
"ROND" 0;
}
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: 1000px) {
.bgSection.open {
width: calc(100vw - 380px);
/* - 2 * var(--paddingSite) */
}
}
/* Cards */
.headerDivKampfrichter {
padding: var(--paddingSite);
background-color: var(--bg-top);
margin-bottom: var(--paddingSite);
position: relative;
z-index: 3;
}
/* headings */
.heading_fv_selturnerin,
.heading_fv_selturnerin>* {
margin-top: 0;
font-weight: 400;
margin-bottom: 20px;
}
.divLiveSyncronisation {
padding: var(--paddingSite);
}
.heading_fv_alleturnerinnen {
margin-top: 0;
font-weight: 400;
}
.headingPanelDiv {
display: flex;
flex-direction: column;
}
.headingPanelDiv>* {
color: var(--bg);
}
.headingPanel {
margin-top: 0;
margin-bottom: 10px;
font-weight: 200;
}
.headingPanelGeraet {
margin: 0;
font-weight: 700;
}
#wsInfo {
margin: 0;
font-weight: 300;
opacity: 0;
transition: all 2s ease;
}
/* Kampfrichter header layout */
.headerDivKampfrichter {
display: flex;
justify-content: space-between;
align-items: center;
}
/* Turnerinnen table */
table {
border-collapse: collapse;
margin: 0;
padding: 0;
width: 100%;
}
.wkvsTabelle th {
color: #424242;
border-bottom: solid 1px #0000007d;
font-weight: 600;
padding: 14px 15px;
font-size: 16px;
}
.wkvsTabelle td {
background-color: #ffffff;
color: #191919;
font-weight: 400;
padding: 14px 15px;
background: none;
text-align: center;
border-bottom: solid 1px #6262624d;
}
.allTurnerinenDiv .widefat input {
min-width: 60px;
display: flex;
margin: auto;
background: none;
text-align: center;
}
.allTurnerinenDiv .editTurnerin,
.allTurnerinenDiv .editTurnerinAdmin {
background: none;
border: none;
cursor: pointer;
}
.allTurnerinenDiv .editTurnerin svg,
.allTurnerinenDiv .editTurnerinAdmin svg {
transition: transform 0.3s ease;
}
.allTurnerinenDiv .editTurnerin svg:hover,
.allTurnerinenDiv .editTurnerinAdmin svg:hover {
transform: scale(1.2);
animation-name: stiftWackler;
animation-duration: 0.5s;
}
@keyframes stiftWackler {
25% {
transform: rotate(-10deg) scale(1.05);
}
75% {
transform: rotate(10deg) scale(1.15);
}
100% {
transform: rotate(0deg) scale(1.2);
}
}
.allTurnerinenDiv tr:hover {
background-color: #f4f4f4;
}
/*.allTurnerinenDiv tr.notHeaderRow:hover td{
-webkit-text-stroke: 0.75px currentColor;
color: currentColor;
}*/
/* Panel with inputs (new style, light) */
.div_edit_values_user {
margin: 2rem auto;
padding: 1.6rem 1.8rem;
background: var(--card-bg);
color: var(--text-main);
flex-direction: column;
align-items: center;
}
/* Titles inside edit card */
.heading_fv_selturnerin {
display: flex;
flex-wrap: wrap;
gap: .35rem;
align-items: baseline;
font-size: 1.15rem;
letter-spacing: 0.01em;
}
.current-turnerin-name {
font-weight: 600;
color: var(--accent);
}
.heading_fv_nextturnerin {
margin: 1.5rem 0 0.75rem;
font-size: 0.9rem;
text-transform: uppercase;
color: var(--text-muted);
letter-spacing: 0.08em;
}
.fv_nextturnerin:hover {
font-size: 1.1rem;
}
/* Tables container inside edit card */
.all_vaules_div,
.all_edit_vaules_div {
display: inline-flex;
/* makes the box shrink to fit */
flex-wrap: wrap;
gap: 1rem;
margin-bottom: 1.25rem;
align-items: center;
}
.all_vaules_div {
flex-direction: column;
}
/* Individual small tables */
.editkampfrichter_user {
border-collapse: collapse;
background: #f9fafb;
border-radius: 12px;
overflow: hidden;
min-width: 180px;
box-shadow: 0 0 0 1px rgba(209, 213, 219, 0.9);
}
.editkampfrichter_user th,
.editkampfrichter_user td {
padding: 0.6rem 0.75rem;
text-align: center;
white-space: nowrap;
}
.editkampfrichter_user th {
font-size: 0.8rem;
text-transform: uppercase;
letter-spacing: 0.09em;
color: var(--text-muted);
background: #f3f4f6;
}
.table_endnote_edit {
margin-top: 1rem;
width: 100%;
}
/* Tight cell for pure input */
.nopadding {
padding: 0.25rem 0.5rem !important;
}
/* Inputs inside edit card */
.fullinput {
width: 100%;
box-sizing: border-box;
/*padding: 0.45rem 0.6rem;*/
border-radius: 8px;
border: 1px solid var(--border-subtle);
background: #ffffff;
color: var(--text-main);
font-size: 0.9rem;
line-height: 1.3;
outline: none;
transition: border-color 0.15s ease, box-shadow 0.15s ease, background-color 0.15s ease;
text-align: center;
}
.fullinput:focus {
border-color: var(--accent);
box-shadow: 0 0 0 1px rgba(45, 115, 172, 0.25);
background: #ffffff;
}
/* Strong & clear disabled style */
.fullinput:disabled,
.fullinput[readonly] {
background: none;
border: none;
color: #6b7280;
opacity: 1;
cursor: auto;
}
.fullinput:disabled:hover,
.fullinput:disabled:focus,
.fullinput[readonly]:hover,
.fullinput[readonly]:focus {
box-shadow: none;
border-color: var(--disabled-border);
}
/* Computed result fields even clearer */
/*[id^="e-note-"]:disabled {
background: #fff7d6;
background: repeating-linear-gradient(
135deg,
#ffefb0,
#ffefb0 6px,
#fff7d6 6px,
#fff7d6 12px
);
border-color: #f59e0b;
box-shadow: inset 0 0 0 1px rgba(245, 158, 11, 0.7);
color: #92400e;
font-weight: 800;
text-align: center;
}*/
[id^="note-user-"]:disabled {
background: none;
border: none;
color: var(--bg-top);
font-weight: 600;
text-align: center;
}
/* Remove number arrows where supported */
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
/* Explanation text */
.editkampfrichter_user_text {
margin-top: 0.9rem;
font-size: 0.85rem;
color: var(--text-muted);
line-height: 1.4;
}
/* Next gymnast link */
.fv_nextturnerin {
display: inline-flex;
align-items: center;
gap: 0.4rem;
min-height: 1.75rem;
font-weight: 500;
color: var(--accent);
text-decoration: none;
border-bottom: 1px dashed rgba(45, 115, 172, 0.45);
padding-bottom: 0.05rem;
transition: all 0.3s;
}
/* Buttons near inputs */
.submit-display-turnerin,
.submit-display-start,
.submit-musik-start,
.submit-musik-stopp,
.submit-display-result {
appearance: none;
border: none;
border-radius: 999px;
padding: 0.5rem 1.1rem;
font-size: 0.9rem;
font-weight: 600;
cursor: pointer;
margin: 0.35rem 0.4rem 0 0;
transition: transform 0.08s ease, box-shadow 0.12s ease, background-color 0.12s ease, color 0.12s ease;
display: inline-flex;
align-items: center;
justify-content: center;
}
/* Variants */
.submit-display-turnerin {
background-color: #378cd243;
border: solid 1px #3382c3;
color: #256396;
}
.submit-display-result {
background-color: #c538d83d;
border: solid 1px #be3bcf;
color: #781467;
}
.submit-display-start,
.submit-musik-start {
background: #ecfdf3;
color: #15803d;
border: 1px solid #4ade80;
}
.submit-musik-stopp {
background: #fef2f2;
color: #b91c1c;
border: 1px solid #fca5a5;
}
/* Button interaction */
.submit-display-turnerin:hover,
.submit-display-start:hover,
.submit-musik-start:hover,
.submit-musik-stopp:hover,
.submit-display-result:hover {
transform: translateY(-1px);
box-shadow: 0 12px 30px rgba(15, 23, 42, 0.12);
}
.submit-display-turnerin:active,
.submit-display-start:active,
.submit-musik-start:active,
.submit-musik-stopp:active,
.submit-display-result:active {
transform: translateY(0);
box-shadow: 0 8px 18px rgba(15, 23, 42, 0.1);
}
/* Burger + menu (unchanged from your style) */
.kampfrichterBurgerMenuDiv {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
cursor: pointer;
z-index: 99;
color: var(--bg);
}
.kampfrichterBurgerMenuDiv svg {
width: 100%;
height: 100%;
stroke: currentColor;
transition: transform 0.3s ease;
}
.kampfrichterBurgerMenuDiv.open svg {
transform: rotate(45deg);
}
.internMenuDiv {
background-color: #fff;
box-shadow: none;
position: fixed;
left: 0;
top: 0;
height: 100svh;
width: 100%;
max-width: 400px;
transform: translateX(-100%);
z-index: 100;
padding: 20px;
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;
min-height: 100vh;
overflow-y: auto;
padding-bottom: 80px;
}
.innerInternMenuDiv h3 {
font-size: 1.1rem;
font-weight: 500;
margin: 10px 0;
color: #1a1a1a;
letter-spacing: 0.5px;
}
.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);
}
.gruppennav {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
background: #f9fafc;
padding: 10px 16px;
border-radius: 12px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.05);
}
.gruppennav p {
margin: 0 6px;
font-weight: 600;
color: #333;
}
.button_gruppe {
border: none;
background: #2d73ac;
color: white;
width: 36px;
height: 36px;
border-radius: 50%;
font-size: 1.1rem;
cursor: pointer;
transition: all 0.3s ease;
box-shadow: 0 2px 6px rgba(0, 0, 0, 0.1);
}
.button_gruppe:hover {
background: #245d8a;
transform: scale(1.05);
}
.labelnamekr {
font-size: 1rem;
font-weight: 500;
margin-top: 14px;
display: block;
color: #222;
}
.inputnamekr {
margin-bottom: 12px;
}
.footerInternMenu {
display: flex;
flex-direction: row;
justify-content: space-between;
position: absolute;
bottom: 12px;
gap: 40px;
width: calc(100% - 40px);
}
.footerInternMenu>* {
display: flex;
}
.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);
}
.menuBg {
position: fixed;
inset: 0;
z-index: 98;
user-select: none;
pointer-events: none;
opacity: 0;
/*background: rgba(0, 0, 0, 0.15);*/
}
.menuBg.menuTransition {
transition: opacity 0.6s ease-out;
}
/*.menuBg.open {
opacity: 1;
pointer-events: auto;
user-select: all;
}*/
.menuTransition {
transition: all 0.6s ease-out;
}
.menuTransition>.kampfrichterBurgerMenuLine {
transition: all 0.3s ease-out;
}
.closeInternMenuMobileDiv {
display: none;
width: 5vw;
height: 100px;
position: absolute;
top: 30px;
right: -5vw;
background-color: #09090966;
border-radius: 0 10px 10px 0;
}
/* Mobile tweaks */
@media (max-width: 480px) {
.internMenuDiv {
max-width: 95vw;
}
.internMenuDiv.open .closeInternMenuMobileDiv {
display: block;
}
}
@media (max-width: 600px) {
.div_edit_values_user,
.allTurnerinenDiv {
padding: var(--paddingSite) calc(var(--paddingSite) * 0.75);
margin-bottom: var(--paddingSite);
}
.notMobile {
display: none;
}
.allTurnerinenDiv th {
padding: 12px 6px;
}
.allTurnerinenDiv td {
padding: 10px 6px;
}
.ranglisteExportBMDesktop {
display: none;
}
.ranglisteExportBMMobile {
display: block;
}
}
@media (min-width: 601px) {
.div_edit_values_user,
.allTurnerinenDiv {
padding: var(--paddingSite) calc(var(--paddingSite) * 1.5);
margin-bottom: var(--paddingSite);
}
.ranglisteExportBMDesktop {
display: block;
}
.ranglisteExportBMMobile {
display: none;
}
}
.buttonNextAbt,
.buttonPrevAbt,
.button_gruppe {
line-height: 0;
}
.all_e_vaules_div {
display: grid;
gap: 1rem;
}
.titleWidthNormalInput {
width: 309.398px;
}
.flexRow {
display: flex;
flex-direction: row;
align-items: center;
gap: 5px;
}
.fullHeightRectangle {
display: flex;
}
.msgDiv {
position: fixed;
bottom: 50px;
right: 20px;
display: flex;
align-items: end;
flex-direction: column-reverse;
gap: 10px;
}
.msgBox {
transform: translateX(calc(100% + 40px));
padding: 10px 15px;
color: #fff;
background-color: #5b5b5b98;
border-left: 4px solid;
border-radius: 2px;
transition: all 0.5s ease;
}
.msgBox.show {
transform: translateX(0);
}
.playcontrolDiv {
display: flex;
justify-content: center;
margin: 20px 0;
}
.noKampfrichterDiv {
display: none;
justify-content: center;
align-items: center;
position: absolute;
top: 0;
left: 0;
width: 100%;
min-height: 100vh;
height: 100%;
background-color: #e9eef6a0;
backdrop-filter: blur(3px);
z-index: 2;
}
.noKampfrichterDiv>h1 {
color: var(--bg-top);
}
.adminButtonDiv input,
.buttonNewAdminStyle {
background-color: var(--bg-top);
color: #fff;
border: none;
border-radius: 5px;
font-weight: 200;
cursor: pointer;
padding: 12px 18px;
transition: all 0.3s ease;
box-shadow: 0 4px 10px rgba(0, 0, 0, 0.1);
}
.adminButtonDiv input {
margin: 10px 0 0 10px;
}
.adminButtonDiv input:hover {
filter: brightness(1.2);
}
.titleSingleAbt {
font-weight: 400;
color: var(--bg-top);
}
.adminButtonDiv {
display: block;
}
table.widefat {
table-layout: auto;
}
.singleAbtDiv:not(:last-child) {
margin-bottom: 60px;
}
.singleEXNoteAdminTable {
display: flex;
flex-direction: row;
}
.singleEXNoteAdminTableLable {
width: 40px;
margin: 3px 0 3px 10px;
text-align: end;
}
.tableWraperOverflowY {
overflow-x: auto;
overflow-y: hidden;
/* Prevent scroll chaining to body */
overscroll-behavior-x: contain;
/* Smooth scrolling on touch devices */
-webkit-overflow-scrolling: touch;
}
.div_edit_values_all_gereate {
display: flex;
flex-direction: row;
flex-wrap: wrap;
gap: 3rem;
width: 100%;
justify-content: space-around;
}
select {
appearance: none;
/* Standard */
-webkit-appearance: none;
/* WebKit */
-moz-appearance: none;
/* Firefox */
width: 100%;
padding: 12px 35px 12px 14px !important;
font-size: 16px;
border: 1px solid #ccc;
border-radius: 4px;
background-color: #fff;
background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'>\
<path d='M6 9L12 15L18 9' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/>\
</svg>");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 20px;
cursor: pointer;
}
select:open {
background-image: url("data:image/svg+xml;utf8,\
<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'>\
<path d='M18 15L12 9L6 15' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/>\
</svg>");
}

View File

@@ -0,0 +1,949 @@
:root {
--paddingSite: 40px;
--card-radius: 20px;
--card-bg: #ffffff;
--bg: #FDFDFD;
--bg-top: rgb(74, 85, 104);
--bg-top-raw: 74 85 104;
--card-shadow: 0 10px 30px rgba(15, 23, 42, 0.08);
--accent: #4a5568;
--accent-soft: #f0f5ff;
--border-subtle: #d4d7e1;
--text-main: #191919;
--text-muted: #5e5e5e;
--disabled-bg: #f3f4f6;
--disabled-border: #cbd5f5;
}
/* ── Base ─────────────────────────────────────────────── */
*,
*::after,
*::before {
box-sizing: border-box;
-webkit-tap-highlight-color: transparent;
font-family: "Google Sans Flex", sans-serif;
font-optical-sizing: auto;
font-style: normal;
font-variation-settings:
"slnt" 0,
"wdth" 100,
"GRAD" 0,
"ROND" 0;
}
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;
}
/* ── Layout ───────────────────────────────────────────── */
.bgSection {
position: relative;
margin-left: auto;
width: 100vw;
box-sizing: border-box !important;
background: none;
}
@media (min-width: 1000px) {
.bgSection.open {
width: calc(100vw - 380px);
}
}
/* ── Header ───────────────────────────────────────────── */
.headerDivTrainer {
padding: var(--paddingSite);
background-color: var(--bg-top);
margin-bottom: var(--paddingSite);
position: relative;
z-index: 3;
display: flex;
justify-content: space-between;
align-items: center;
}
.headingPanelDiv {
display: flex;
flex-direction: column;
}
.headingPanelDiv>* {
color: var(--bg);
}
.headingPanel {
margin: 0;
font-weight: 200;
}
/* ── Actions bar (new user / new verein) ──────────────── */
.bg-erfassen {
display: flex;
flex-wrap: wrap;
gap: 12px;
padding: 0 var(--paddingSite) 10px;
}
.bg-erfassen button[type="submit"] {
appearance: none;
border: 1px dashed var(--accent);
background: none;
color: var(--text-main);
padding: 10px 22px;
border-radius: 8px;
font-size: 0.95rem;
font-weight: 500;
cursor: pointer;
transition: all 0.25s ease;
}
.bg-erfassen button[type="submit"]:hover {
background: var(--bg-top);
border-color: var(--bg-top);
color: #fff;
}
/* ── Section headings (Vereine / Benutzer) ────────────── */
h3.vereine,
h3.benutzer {
padding: 0 var(--paddingSite);
font-weight: 400;
font-size: 1.15rem;
color: var(--text-main);
margin: 30px 0 12px;
letter-spacing: 0.02em;
}
/* ── Inner section (card list) ────────────────────────── */
.inner-pw-set-bg {
padding: 0 var(--paddingSite);
display: flex;
flex-direction: column;
gap: 14px;
margin-bottom: 20px;
align-items: start;
}
/* ── User / Verein card ───────────────────────────────── */
.single_pwedit {
background: var(--card-bg);
border-radius: var(--card-radius);
box-shadow: var(--card-shadow);
padding: 24px 28px;
transition: box-shadow 0.25s ease;
}
.single_pwedit:hover {
box-shadow: 0 14px 40px rgba(15, 23, 42, 0.12);
}
.single_pwedit.verein {
border-left: 4px solid var(--bg-top);
}
/* ── Form fields inside user card ─────────────────────── */
.single_pwedit form {
display: flex;
flex-wrap: wrap;
gap: 14px 24px;
align-items: flex-end;
}
.single_pwedit label {
display: block;
font-size: 0.8rem;
font-weight: 500;
text-transform: uppercase;
letter-spacing: 0.06em;
color: var(--text-muted);
margin-bottom: 5px;
}
.single_pwedit .field-group {
flex: 1 1 200px;
min-width: 0;
}
.single_pwedit input[type="text"] {
width: 100%;
padding: 9px 12px;
border: 1px dashed #999;
border-radius: 6px;
font-size: 0.95rem;
font-weight: 300;
color: var(--text-main);
background: #fff;
transition: border-color 0.2s ease, box-shadow 0.2s ease;
}
.single_pwedit input[type="text"]:focus {
outline: none;
border: 1px solid var(--bg-top);
box-shadow: 0 0 0 3px rgba(var(--bg-top-raw) / 0.12);
}
.single_pwedit input[type="text"]::placeholder {
color: #b0b0b0;
font-style: italic;
}
/* ── Buttons inside cards ─────────────────────────────── */
.single_pwedit button[type="submit"] {
appearance: none;
border: 1px solid #7777778e;
background: #7777778e;
color: var(--bg);
padding: 9px 20px;
border-radius: 6px;
font-size: 0.9rem;
font-weight: 400;
cursor: pointer;
transition: all 0.25s ease;
white-space: nowrap;
}
.single_pwedit button[type="submit"]:hover {
background: var(--bg-top);
border-color: var(--bg-top);
color: #fff;
font-weight: 600;
}
.delete-user-btn,
.delete-verein-btn {
appearance: none;
background: none;
border: 1px solid #fca5a5;
color: #b91c1c;
padding: 8px 18px;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
margin-top: 10px;
transition: all 0.25s ease;
}
.delete-user-btn:hover,
.delete-verein-btn:hover {
background: #fef2f2;
border-color: #ef4444;
color: #dc2626;
}
.createOturl {
appearance: none;
background: none;
border: 1px solid var(--border-subtle);
color: var(--text-muted);
padding: 8px 18px;
border-radius: 6px;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
margin-top: 10px;
margin-left: 8px;
transition: all 0.25s ease;
}
.createOturl:hover {
background: var(--accent-soft);
border-color: var(--bg-top);
color: var(--bg-top);
}
/* ── Permission dropdown ──────────────────────────────── */
.perm-section {
width: 100%;
margin-top: 12px;
border: 1px solid #e5e7eb;
border-radius: 10px;
overflow: hidden;
transition: box-shadow 0.2s ease;
}
.perm-section:hover {
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.04);
}
.perm-section-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 16px;
background: #f9fafb;
cursor: pointer;
user-select: none;
transition: background 0.15s ease;
}
.perm-section-header:hover {
background: #f3f4f6;
}
.perm-section-title {
font-size: 0.85rem;
font-weight: 600;
color: var(--text-main);
display: flex;
align-items: center;
gap: 8px;
}
.perm-section-title .perm-badge {
display: inline-flex;
align-items: center;
justify-content: center;
min-width: 22px;
height: 22px;
padding: 0 6px;
border-radius: 11px;
background: var(--bg-top);
color: #fff;
font-size: 0.7rem;
font-weight: 700;
}
.perm-section-chevron {
width: 20px;
height: 20px;
transition: transform 0.3s ease;
color: var(--text-muted);
}
.perm-section.open .perm-section-chevron {
transform: rotate(180deg);
}
.perm-section-body {
max-height: 0;
overflow: hidden;
transition: max-height 0.35s cubic-bezier(0.4, 0, 0.2, 1),
padding 0.35s cubic-bezier(0.4, 0, 0.2, 1);
padding: 0 16px;
}
.perm-section.open .perm-section-body {
max-height: 500px;
padding: 12px 16px 16px;
}
.perm-section-body label {
display: flex;
align-items: center;
gap: 8px;
padding: 6px 0;
font-size: 0.9rem;
font-weight: 400;
color: var(--text-main);
text-transform: none;
letter-spacing: 0;
cursor: pointer;
transition: color 0.15s ease;
}
.perm-section-body label:hover {
color: var(--bg-top);
}
.perm-section-body input[type="checkbox"] {
width: 16px;
height: 16px;
accent-color: var(--bg-top);
cursor: pointer;
flex-shrink: 0;
}
/* ── Programme section ────────────────────────────────── */
section:has(> div > h1) {
padding: 0 var(--paddingSite) var(--paddingSite);
}
section h1 {
font-weight: 400;
font-size: 1.3rem;
color: var(--text-main);
margin-bottom: 16px;
}
.neuProgrammForm {
display: flex;
gap: 12px;
margin-bottom: 20px;
align-items: center;
}
.neuProgrammForm input[type="text"] {
padding: 9px 12px;
border: 1px dashed #999;
border-radius: 6px;
font-size: 0.95rem;
font-weight: 300;
color: var(--text-main);
min-width: 200px;
transition: border-color 0.2s ease;
}
.neuProgrammForm input[type="text"]:focus {
outline: none;
border-color: var(--bg-top);
}
.neuProgrammForm input[type="submit"] {
padding: 9px 20px;
border: 1px solid #7777778e;
background: #7777778e;
color: var(--bg);
border-radius: 6px;
font-size: 0.9rem;
cursor: pointer;
transition: all 0.25s ease;
}
.neuProgrammForm input[type="submit"]:hover {
background: var(--bg-top);
border-color: var(--bg-top);
color: #fff;
}
/* ── Programme table ──────────────────────────────────── */
table {
border-collapse: collapse;
margin: 0;
padding: 0;
width: 100%;
}
.wkvsTabelle th {
color: #424242;
border-bottom: solid 1px #0000007d;
font-weight: 600;
padding: 14px 15px;
font-size: 16px;
}
.wkvsTabelle td {
background: none;
color: #191919;
font-weight: 400;
padding: 10px 15px;
border-bottom: solid 1px #6262624d;
}
.wkvsTabelle.trainer td {
padding: 8px 15px;
}
.wkvsTabelle tr:hover {
background-color: #f4f4f4;
}
.inputPreisProgramm,
.inputAktivProgramm {
padding: 6px 10px;
border: 1px dashed #999;
border-radius: 4px;
font-size: 0.9rem;
transition: border-color 0.2s ease, background-color 0.4s ease;
}
.inputPreisProgramm:focus,
.inputAktivProgramm:focus {
outline: none;
border-color: var(--bg-top);
}
input[type="number"] {
-moz-appearance: textfield;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.deleteProgramm {
appearance: none;
background: none;
border: none;
cursor: pointer;
}
select {
appearance: none;
-webkit-appearance: none;
-moz-appearance: none;
width: 100%;
padding: 8px 35px 8px 12px !important;
font-size: 14px;
border: 1px solid #ccc;
border-radius: 6px;
background-color: #fff;
background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none'><path d='M6 9L12 15L18 9' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/></svg>");
background-repeat: no-repeat;
background-position: right 10px center;
background-size: 18px;
cursor: pointer;
}
/* ── One-time URL Modal ───────────────────────────────── */
.ot-modal {
display: none;
position: fixed;
inset: 0;
background: rgba(0, 0, 0, 0.4);
backdrop-filter: blur(4px);
z-index: 9999;
align-items: center;
justify-content: center;
}
.ot-modal-content {
background: #fff;
border-radius: 16px;
padding: 28px 32px;
max-width: 520px;
width: 90%;
box-shadow: 0 20px 60px rgba(0, 0, 0, 0.2);
}
.ot-modal-top-content {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 16px;
}
.ot-title {
font-size: 1.1rem;
font-weight: 600;
color: var(--text-main);
}
.ot-close-btn {
appearance: none;
background: none;
border: none;
font-size: 1.3rem;
color: #888;
cursor: pointer;
transition: color 0.2s ease;
padding: 4px 8px;
}
.ot-close-btn:hover {
color: #d63638;
}
.ot-url {
display: block;
word-break: break-all;
padding: 12px 16px;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
color: var(--bg-top);
font-size: 0.85rem;
font-weight: 500;
margin-bottom: 12px;
text-decoration: none;
transition: background 0.2s ease;
}
.ot-url:hover {
background: #f0f1f3;
}
#oturlGb {
font-size: 0.82rem;
color: var(--text-muted);
margin: 0 0 16px;
}
.ot-copy-btn {
appearance: none;
width: 100%;
padding: 10px;
background: var(--bg-top);
color: #fff;
border: none;
border-radius: 8px;
font-size: 0.95rem;
font-weight: 600;
cursor: pointer;
transition: filter 0.2s ease;
}
.ot-copy-btn:hover {
filter: brightness(1.15);
}
/* ── Success / Error Overlays ─────────────────────────── */
.divSucsess,
.divError {
position: fixed;
bottom: 30px;
right: 30px;
padding: 14px 28px;
border-radius: 10px;
font-weight: 600;
font-size: 0.95rem;
z-index: 10000;
transform: translateX(calc(100% + 60px));
transition: transform 0.4s cubic-bezier(0.4, 0, 0.2, 1);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
}
.divSucsess {
background: #ecfdf3;
color: #15803d;
border-left: 4px solid #4ade80;
}
.divError {
background: #fef2f2;
color: #b91c1c;
border-left: 4px solid #fca5a5;
}
.divSucsess.show,
.divError.show {
transform: translateX(0);
}
/* ── Loading overlays ─────────────────────────────────── */
#loading-overlay,
#new-overlay,
#new-reload {
display: none;
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: 9999;
text-align: center;
}
#loading-overlay,
#new-overlay {
background: rgba(0, 0, 0, 0.5);
}
#new-reload {
background: rgba(0, 100, 0, 0.5);
}
#loading-overlay>div,
#new-overlay>div,
#new-reload>div {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: white;
font-size: 1.2rem;
font-weight: 400;
}
/* ── Burger / sidebar (shared with other pages) ───────── */
.trainerBurgerMenuDiv {
display: flex;
align-items: center;
justify-content: center;
width: 28px;
height: 28px;
cursor: pointer;
z-index: 99;
color: var(--bg);
}
.trainerBurgerMenuDiv svg {
width: 100%;
height: 100%;
stroke: currentColor;
transition: transform 0.3s ease;
}
.trainerBurgerMenuDiv.open svg {
transform: rotate(-90deg);
}
.internMenuDiv {
background-color: #fff;
box-shadow: none;
position: fixed;
left: 0;
top: 0;
height: 100svh;
width: 100%;
max-width: 380px;
transform: translateX(-100%);
z-index: 100;
padding: 20px;
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: 100dvh;
overflow-y: auto;
padding-bottom: 80px;
}
.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: var(--bg-top);
box-shadow: 0 0 6px rgba(var(--bg-top-raw) / 0.3);
}
.innerInternMenuDiv .button-secondary {
background: linear-gradient(135deg, var(--bg-top), #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 {
filter: brightness(0.9);
transform: translateY(-2px);
}
.footerInternMenu {
display: flex;
flex-direction: row;
justify-content: space-between;
position: absolute;
bottom: 12px;
gap: 40px;
width: calc(100% - 40px);
}
.footerInternMenu>* {
display: flex;
}
.menuBg {
position: fixed;
inset: 0;
z-index: 98;
user-select: none;
pointer-events: none;
opacity: 0;
}
.menuBg.menuTransition {
transition: opacity 0.6s ease-out;
}
.menuTransition {
transition: all 0.6s ease-out;
}
.closeInternMenuMobileDiv {
display: none;
width: 5vw;
height: 100px;
position: absolute;
top: 30px;
right: -5vw;
background-color: #09090966;
border-radius: 0 10px 10px 0;
}
/* ── Responsive ───────────────────────────────────────── */
@media (max-width: 480px) {
.internMenuDiv {
max-width: 95vw;
}
.internMenuDiv.open .closeInternMenuMobileDiv {
display: block;
}
}
@media (max-width: 600px) {
:root {
--paddingSite: 20px;
}
.single_pwedit {
padding: 18px 16px;
border-radius: 14px;
}
.single_pwedit form {
flex-direction: column;
}
.single_pwedit .field-group {
flex: 1 1 100%;
}
.bg-erfassen {
flex-direction: column;
}
}
@media (min-width: 768px) {
.inner-pw-set-bg {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(420px, 1fr));
}
}
.checkbox {
display: inline-flex;
align-items: center;
gap: 8px;
cursor: pointer;
position: relative;
}
.checkbox input {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
margin: 0;
opacity: 0;
cursor: pointer;
pointer-events: all;
}
/* Custom box */
.checkbox-ui {
width: 16px;
height: 16px;
background: rgb(212, 212, 212);
border-radius: 3px;
display: inline-flex;
align-items: center;
justify-content: center;
transition: background 0.2s, border-color 0.2s;
}
/* Checked state */
.checkbox input:checked+.checkbox-ui,
.checkbox-ui.manuelChecked {
background: #1cde60ff;
}
/* Focus state */
.checkbox input:focus-visible+.checkbox-ui {
outline: 2px solid #93c5fd;
outline-offset: 2px;
}
.addRow input {
padding: 6px 10px;
border: 1px dashed var(--bg-top);
border-radius: 4px;
font-size: 0.9rem;
width: 100%;
}
.addRow button {
padding: 6px 14px;
border: 1px solid var(--bg-top);
background: var(--bg-top);
color: #fff;
border-radius: 4px;
font-size: 0.85rem;
font-weight: 500;
cursor: pointer;
transition: all 0.2s;
}
.containerSection {
padding: var(--paddingSite) calc(var(--paddingSite) * 1.5);
margin-bottom: var(--paddingSite);
}
#qrcode {
display: inline-flex;
padding: 12px 16px;
background: #f9fafb;
border: 1px solid #e5e7eb;
border-radius: 8px;
color: var(--bg-top);
font-size: 0.85rem;
font-weight: 500;
margin-bottom: 12px;
text-decoration: none;
transition: background 0.2s ease;
}
.qrcodeWrapper {
display: flex;
align-items: center;
justify-content: center;
margin: 30px 0;
}

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,509 @@
:root {
--main: #fe3f18;
--bg-top-raw: 254 63 24;
--main-box-shadow: rgb(from var(--main) r g b / 0.05);
--accent: #cfef00;
--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;
}
/* --- 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: linear-gradient(135deg, var(--main), #d85a00);
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;
}
.allTurnerinenDiv {
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: 20px;
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: 100dvh;
overflow-y: auto;
padding-bottom: 80px;
}
.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;
}
.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 */
.header-text-conatiner {
display: flex;
flex-direction: column;
padding: 20px;
gap: 15px;
}
.header-text-conatiner button {
cursor: pointer;
font-weight: 600;
transition: all 0.2s ease;
padding: 12px 20px;
border-radius: 100px;
background: var(--accent);
border: 1px solid transparent;
font-size: 15px;
color: #000;
width: 100%;
box-shadow: 0 4px 6px rgba(207, 239, 0, 0.2);
}
.header-text-conatiner button:hover {
background: var(--accent-hover);
transform: translateY(-2px);
box-shadow: 0 6px 12px rgba(207, 239, 0, 0.3);
}
.header-text-conatiner button:active {
transform: scale(0.98) translateY(0);
}
.header-text-conatiner label {
font-weight: 400;
color: var(--text-main);
font-size: 0.9rem;
margin-bottom: -5px;
}
.header-text-conatiner 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%;
}
.header-text-conatiner 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;
}
.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: 20px;
border-radius: 12px;
margin: 20px 20px;
}
.invalidAbtDiv .geraet-table {
box-shadow: none;
border-color: #fecdd3;
width: 100%;
}
.invalidAbtDiv .geraet-table thead {
background: #ffe4e6;
}
.invalidAbtDiv .geraet-table thead th {
color: #e11d48;
}
/* Horizontal scroll area for departments */
.allTurnerinenDiv>div {
padding-bottom: 20px;
}
.allTurnerinenDiv>div>div:last-child {
overflow-x: auto;
padding: 10px 0 20px 0;
}
.allTurnerinenDiv>div>div:last-child::-webkit-scrollbar {
height: 8px;
}
.allTurnerinenDiv>div>div:last-child::-webkit-scrollbar-track {
background: #f1f5f9;
border-radius: 10px;
}
.allTurnerinenDiv>div>div:last-child::-webkit-scrollbar-thumb {
background: #cbd5e1;
border-radius: 10px;
}

341
www/intern/css/sidebar.css Normal file
View File

@@ -0,0 +1,341 @@
/* ─── Sidebar Navigation ─── */
:root {
--sidebar-width: 280px;
--sidebar-bg: #fafafa;
--sidebar-text: #363636;
--sidebar-hover: rgb(var(--bg-top-raw) / 0.2);
--sidebar-active: rgb(var(--bg-top-raw) / 0.35);
--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);
display: flex;
flex-direction: column;
overflow-y: auto;
box-shadow: 4px 0 24px rgba(0, 0, 0, 0.25);
}
.sidebar-nav.open {
transform: translateX(0);
}
/* 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: #111;
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(45, 45, 45, 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: #222;
}
.sidebar-links a.active {
background: var(--sidebar-active);
color: #111;
border-left-color: var(--bg-top);
}
.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: var(--sidebar-bg);
border: none;
border-radius: 10px;
width: 44px;
height: 44px;
cursor: pointer;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
gap: 5px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.2);
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(42, 42, 42, 0.222);
font-size: 11px;
color: rgba(42, 42, 42, 0.485);
}
.menuWrapper {
display: flex;
align-items: center;
gap: 16px;
}
.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: rgba(45, 45, 45, 0.85);
}
.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;
}
.sidebar-nav .selectOptions,
.sidebar-nav .selectOptionsBulk {
position: absolute;
top: calc(100% + 4px);
left: 0;
right: 0;
padding: 4px;
list-style: none;
border: 1px solid;
border-radius: 12px;
background: var(--sidebar-bg);
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;
}
.sidebar-nav .customSelect.open .selectOptions,
.sidebar-nav .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;
}
/* ─── 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: rgba(45, 45, 45, 0.45);
margin-bottom: 6px;
letter-spacing: 0.5px;
}
.sidebar-li-freigaben .selectTrigger {
width: 100%;
padding: 9px 12px;
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.08);
border-radius: 8px;
font-size: 13px;
font-weight: 500;
color: var(--sidebar-text);
transition: all 0.2s ease;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.02);
}
.sidebar-li-freigaben .selectTrigger:hover {
border-color: rgba(0, 0, 0, 0.15);
background: #fcfcfc;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.04);
}
.sidebar-li-freigaben .selectOptions {
background: #fff;
border: 1px solid rgba(0, 0, 0, 0.1);
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.12);
margin-top: 2px;
}
.sidebar-li-freigaben .selectOptions li {
font-size: 13px;
padding: 8px 12px;
}
.sidebar-li-freigaben .selectOptions li.selected {
background: var(--sidebar-active);
color: #111;
font-weight: 600;
}

1609
www/intern/css/trainer.css Normal file

File diff suppressed because it is too large Load Diff

77
www/intern/css/wkvs.css Normal file
View File

@@ -0,0 +1,77 @@
.welcomeScreen{
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
z-index: 10000;
background-color: #fff;
display: flex;
align-items: center;
justify-content: center;
}
.welcomeScreen{
animation: fadeOut 0.9s forwards;
animation-delay: 3s;
}
.innerWelcomeScreen{
display: flex;
flex-direction: column;
}
.textWelcomeScreen{
text-align: center;
}
.textWelcomeScreen.text span {
display: inline-block;
opacity: 0;
transform: translateY(20px);
animation: rise 0.5s forwards;
font-size: 60px;
font-weight: 100;
color: #8044A6;
}
.textWelcomeScreen.title span {
display: inline-block;
opacity: 0;
transform: translateY(20px);
animation: fall 0.9s forwards;
font-size: 120px;
}
@keyframes rise {
0% {
opacity: 0;
transform: translateY(20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}
@keyframes fadeOut {
0% {
opacity: 1;
}
99% {
opacity: 0;
}
100% {
display: none;
}
}
@keyframes fall {
0% {
opacity: 0;
transform: translateY(-20px);
}
100% {
opacity: 1;
transform: translateY(0);
}
}

BIN
www/intern/img/icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 155 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 127 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

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: 53 KiB

1
www/intern/img/readme.md Normal file
View File

@@ -0,0 +1 @@
Die Intern Seiten suchen das Icon in diesem Order mit dem Namen icon.png

View File

@@ -0,0 +1,97 @@
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);
}
/* function hasSessionCookie(callback) {
$.ajax({
url: '/myjupa/api/check-php-session.php',
method: 'GET',
cache: false, // Ensure we don't get a cached result
success: function(response) {
callback(response.cookieExists);
},
error: function() {
callback(false);
}
});
}
// Usage in your click handler
$(document).on('click', 'a[href*="/myjupa/"]', function (el) {
el.preventDefault();
const target = $(this).attr("href");
hasSessionCookie(function(exists) {
if (exists) {
window.location.assign(target);
} else {
deleteCookieBanner();
displayCookieBanner(target);
}
});
}); */

View File

@@ -0,0 +1,90 @@
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);
}
});
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;
const colors = ["rgb(255, 0, 0)", "rgb(0, 255, 0)", "#ff9d00"];
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 displayConfirm(type, msg) {
return new Promise((resolve) => {
const colors = ["rgb(255, 0, 0)", "rgb(0, 255, 0)", "#ff9d00"];
if (![0, 1, 2].includes(type)) return resolve(false);
const $div = $('<div class="confirmBox"></div>')
.css({ 'border-color': colors[type] })
.text(msg);
const $buttonDiv = $('<div class="buttonConfirmDiv"></div>');
const $buttonYes = $('<button class="confirmYesButton">Ja</button>');
const $buttonNo = $('<button class="confirmNoButton">Nein</button>');
$buttonDiv.append($buttonNo, $buttonYes);
$div.append($buttonDiv);
$msgDiv.append($div);
$buttonYes.on('click', function () {
fadeOutDiv($div);
resolve(true);
});
$buttonNo.on('click', function () {
fadeOutDiv($div);
resolve(false);
});
setTimeout(() => $div.addClass('show'), 50);
});
}

View File

@@ -0,0 +1,68 @@
$(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);
$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
www/intern/js/flatpickr/de.min.js vendored Normal file
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
www/intern/js/flatpickr/fr.min.js vendored Normal file
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
www/intern/js/flatpickr/it.min.js vendored Normal file
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
www/intern/js/gallery.js Normal file
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();
});
});

52
www/intern/js/header.js Normal file
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
www/intern/js/img.js Normal file
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

6
www/intern/js/jquery/jquery-ui.min.js vendored Normal file

File diff suppressed because one or more lines are too long

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

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.

View File

@@ -0,0 +1,46 @@
# QRCode.js
QRCode.js is javascript library for making QRCode. QRCode.js supports Cross-browser with HTML5 Canvas and table tag in DOM.
QRCode.js has no dependencies.
## Basic Usages
```
<div id="qrcode"></div>
<script type="text/javascript">
new QRCode(document.getElementById("qrcode"), "http://jindo.dev.naver.com/collie");
</script>
```
or with some options
```
<div id="qrcode"></div>
<script type="text/javascript">
var qrcode = new QRCode(document.getElementById("qrcode"), {
text: "http://jindo.dev.naver.com/collie",
width: 128,
height: 128,
colorDark : "#000000",
colorLight : "#ffffff",
correctLevel : QRCode.CorrectLevel.H
});
</script>
```
and you can use some methods
```
qrcode.clear(); // clear the code.
qrcode.makeCode("http://naver.com"); // make another code.
```
## Browser Compatibility
IE6~10, Chrome, Firefox, Safari, Opera, Mobile Safari, Android, Windows Mobile, ETC.
## License
MIT License
## Contact
twitter @davidshimjs
[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/davidshimjs/qrcodejs/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

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"
]
}

View File

@@ -0,0 +1,47 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
<title>Cross-Browser QRCode generator for Javascript</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="qrcode.js"></script>
</head>
<body>
<input id="text" type="text" value="http://jindo.dev.naver.com/collie" style="width:80%" />
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="qrcode"/>
</svg>
<script type="text/javascript">
var qrcode = new QRCode(document.getElementById("qrcode"), {
width : 100,
height : 100,
useSVG: true
});
function makeCode () {
var elText = document.getElementById("text");
if (!elText.value) {
alert("Input a text");
elText.focus();
return;
}
qrcode.makeCode(elText.value);
}
makeCode();
$("#text").
on("blur", function () {
makeCode();
}).
on("keydown", function (e) {
if (e.keyCode == 13) {
makeCode();
}
});
</script>
</body>
</html>

View File

@@ -0,0 +1,44 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ko" lang="ko">
<head>
<title>Cross-Browser QRCode generator for Javascript</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width,initial-scale=1,user-scalable=no" />
<script type="text/javascript" src="jquery.min.js"></script>
<script type="text/javascript" src="qrcode.js"></script>
</head>
<body>
<input id="text" type="text" value="http://jindo.dev.naver.com/collie" style="width:80%" /><br />
<div id="qrcode" style="width:100px; height:100px; margin-top:15px;"></div>
<script type="text/javascript">
var qrcode = new QRCode(document.getElementById("qrcode"), {
width : 100,
height : 100
});
function makeCode () {
var elText = document.getElementById("text");
if (!elText.value) {
alert("Input a text");
elText.focus();
return;
}
qrcode.makeCode(elText.value);
}
makeCode();
$("#text").
on("blur", function () {
makeCode();
}).
on("keydown", function (e) {
if (e.keyCode == 13) {
makeCode();
}
});
</script>
</body>

View File

@@ -0,0 +1,37 @@
<?xml version="1.0" standalone="yes"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="-50 0 200 100">
<g id="qrcode"/>
<foreignObject x="-50" y="0" width="100" height="100">
<body xmlns="http://www.w3.org/1999/xhtml" style="padding:0; margin:0">
<div style="padding:inherit; margin:inherit; height:100%">
<textarea id="text" style="height:100%; width:100%; position:absolute; margin:inherit; padding:inherit">james</textarea>
</div>
<script type="application/ecmascript" src="qrcode.js"></script>
<script type="application/ecmascript">
var elem = document.getElementById("qrcode");
var qrcode = new QRCode(elem, {
width : 100,
height : 100
});
function makeCode () {
var elText = document.getElementById("text");
if (elText.value === "") {
//alert("Input a text");
//elText.focus();
return;
}
qrcode.makeCode(elText.value);
}
makeCode();
document.getElementById("text").onkeyup = function (e) {
makeCode();
};
</script>
</body>
</foreignObject>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

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;
})();

1
www/intern/js/qr-code/qrcode.min.js vendored Normal file

File diff suppressed because one or more lines are too long

104
www/intern/js/sidebar.js Normal file
View File

@@ -0,0 +1,104 @@
(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);
fetch('/intern/scripts/kampfrichter/ajax/ajax-update_selected_kampfrichter.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 .selectTrigger', function (e) {
e.stopPropagation();
$(this).closest('.customSelect').toggleClass('open');
});
$(document).on('click', '.sidebar-nav .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);
$container.removeClass('open');
changeFreigabe(val);
});
$(document).on('click', () => $('.sidebar-nav .customSelect').removeClass('open'));

1198
www/intern/kampfrichter.php Normal file

File diff suppressed because it is too large Load Diff

433
www/intern/otlogin.php Normal file
View File

@@ -0,0 +1,433 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
use Dotenv\Dotenv;
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$oturl = $_GET['otl'] ?? '';
if (!$oturl) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$error = '';
function logIn() {
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;
}
$type = 'otl';
// DB
$dbconnection = require $baseDir .'/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
return "DB Error";
}
require $baseDir . '/../scripts/db/db-tables.php';
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']);
$password2 = trim($_POST['password2']);
if ($password === '' || $password2 === '') {
return 'Beide Felder müssen ausgefüllt sein';
}
if ($password !== $password2) {
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);
// update password
$updateResult = db_update($mysqli, $tableInternUsers, ['password_hash' => $hash, 'password_cipher' => $cipher_store, '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 $tableOTL WHERE id = ?");
$stmt->bind_param("i", $dbid);
if (!$stmt->execute()) {
return "DB Error";
}
$stmt->close();
$sql = "SELECT freigabe FROM $tableInternUsers WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $iduser);
$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['passcode'.$freigabeType.'_id'] = $iduser;
}
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.';
}
}
/* ============================================================
PASSWORD SET ON POST
============================================================ */
if (isset($_POST['password1'], $_POST['password2'], $_POST['setpasswordbtn'], $_POST['user_id'])) { $error = logIn() ?? ''; }
/* ============================================================
ONE-TIME-LOGIN VALIDATION (GET)
============================================================ */
$token = "QQa2UMbEYW8oOL7wz9DjtqECVCikSZsDuSdmzxiadEXFsKyujEUyQOW1AYMD2OqU8VXxClIRweRuWLzvBrZpPYL41e89Rs96tM7Lq1KpjA5E2mg2UfgvztheGRV";
require $baseDir .'/../scripts/db/db-verbindung-script-guest.php';
// fetch one-time login record
$result = db_select(
$guest,
$tableOTL,
'id, user_id',
'url = ? AND timestamp >= NOW() - INTERVAL 24 HOUR',
[$oturl]
);
if (!$result || count($result) !== 1) {
http_response_code(403);
exit;
}
$dbid = intval($result[0]['id']);
$iduser = intval($result[0]['user_id']);
// store dbid for later deletion
$_SESSION['otl_dbid'] = $dbid;
$tableusers = 'wp_secure_lock';
// fetch user
$userinfo = db_select($guest, $tableInternUsers, 'username', 'id = ?', [$iduser]);
$guest->close();
if (!$userinfo || count($userinfo) !== 1) {
echo 'Ungültige Benutzerinformationen';
exit;
}
$username = $userinfo[0]['username'];
// set session token that grants password reset
$_SESSION['set_new_password_id_user'] = $iduser;
$_SESSION['set_new_password_granted'] = true;
?>
<!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">
<script src="/intern/js/custom-msg-display.js"></script>
</head>
<body>
<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">
<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>
<style>
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;
}
</style>
<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>*/
?>

View File

@@ -0,0 +1,94 @@
<?php
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
$protocol = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') ? "https://" : "http://";
$domain = $_SERVER['HTTP_HOST'];
$url = $protocol . $domain;
if ((isset($_POST['prev_abt'])) && !empty($_POST['prev_abt_submit'])) {
$value = get_option('wk_panel_current_abt', 1);
if ($value > 1){
$value -= 1;
update_option('wk_panel_current_abt', $value);
}
header("Location: ".$url."/intern/kampfrichter".$_SERVER['REQUEST_URI']);
exit;
}
if ((isset($_POST['next_abt'])) && !empty($_POST['next_abt_submit'])) {
$value = get_option('wk_panel_current_abt', 1);
$maxvalue = $wpdb->get_var( "SELECT abteilung FROM $table_name ORDER BY abteilung DESC LIMIT 1" );
if ($value < $maxvalue){
$value += 1;
update_option('wk_panel_current_abt', $value);
}
header("Location: ".$url."/intern/kampfrichter".$_SERVER['REQUEST_URI']);
exit;
}
if (!isset($_SESSION['currentsubabt'])){
$_SESSION['currentsubabt'] = 0;
}
if (!isset($_SESSION['last_abt'])){
$_SESSION['last_abt'] = get_option('wk_panel_current_abt', 1);
}
if ($_SESSION['last_abt'] !== get_option('wk_panel_current_abt', 1)){
$_SESSION['currentsubabt'] = 0;
$_SESSION['last_abt'] = get_option('wk_panel_current_abt', 1);
}
if ((isset($_POST['prev_subabt'])) && !empty($_POST['prev_subabt_submit'])) {
$value = $_SESSION['currentsubabt'];
if ($value > 0){
$_SESSION['currentsubabt']--;
$_SESSION['last_abt'] = get_option('wk_panel_current_abt', 1);
}
header("Location: ".$url."/intern/kampfrichter");
exit;
}
if ((isset($_POST['next_subabt'])) && !empty($_POST['next_subabt_submit'])) {
$value = $_SESSION['currentsubabt'];
if ($value < 4){
$_SESSION['currentsubabt']++;
$_SESSION['last_abt'] = get_option('wk_panel_current_abt', 1);
}
header("Location: ".$url."/intern/kampfrichter");
exit;
}
if (
isset($_POST['togle_advanced_mode']) &&
!empty($_POST['togle_advanced_mode_submit']) &&
check_admin_referer('toggle_advanced_mode_action', 'toggle_advanced_mode_nonce')
) {
$current_value = get_option('option_advanced_mode', false);
$new_value = !$current_value;
update_option('option_advanced_mode', $new_value);
hheader("Location: ".$url."/intern/kampfrichter".$_SERVER['REQUEST_URI']);
exit;
}
if (
isset($_POST['togle_advanced_mode_admin']) &&
!empty($_POST['togle_advanced_mode_admin_submit']) &&
check_admin_referer('toggle_advanced_mode_admin_action', 'toggle_advanced_mode_admin_nonce')
) {
$current_value = get_option('option_advanced_mode_admin', false);
$new_value = !$current_value;
update_option('option_advanced_mode_admin', $new_value);
header("Location: ".$url."/intern/kampfrichter".$_SERVER['REQUEST_URI']);
exit;
}

View File

@@ -0,0 +1,28 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (
!((isset($_SESSION['access_granted_wk_leitung']) && $_SESSION['access_granted_wk_leitung'] === true) ||
(isset($_SESSION['access_granted_kampfrichter']) && $_SESSION['access_granted_kampfrichter'] === true))
) {
http_response_code(403);
exit;
}
if (!isset($_POST['access'])) {
http_response_code(400);
exit;
}
$access = preg_replace("/[\W]/", "", trim($_POST['access']));
$baseDir = $_SERVER['DOCUMENT_ROOT'];
require $baseDir . "/../scripts/websocket/ws-create-token.php";
$token = generateWSToken($access);
$responseBool = $token != null;
echo json_encode(['success' => $responseBool, 'token' => $token]);

View File

@@ -0,0 +1,4 @@
upload_max_filesize = 50M
post_max_size = 55M
max_execution_time = 120
max_input_time = 120

View File

@@ -0,0 +1,142 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$isTrainer =
isset($_SESSION['access_granted_trainer'], $_SESSION['passcodetrainer_id']) &&
$_SESSION['access_granted_trainer'] === true &&
(int)$_SESSION['passcodetrainer_id'] > 0;
$isWkLeitung =
isset($_SESSION['access_granted_wk_leitung'], $_SESSION['passcodewk_leitung_id']) &&
$_SESSION['access_granted_wk_leitung'] === true &&
(int)$_SESSION['passcodewk_leitung_id'] > 0;
if (!$isTrainer && !$isWkLeitung) {
http_response_code(403);
exit;
}
// Allow large uploads and enough memory for GD processing
ini_set('memory_limit', '256M');
ini_set('max_execution_time', '120');
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
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 = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['maxLengthMusic']);
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 $tableAudiofiles (`file_name`,`file_path`) VALUES (?, ?)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ss", $originalName, $normalPath);
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
]);

View File

@@ -0,0 +1,10 @@
-- 1. Update the Noten table to include run_number
-- NOTE: We also update the PRIMARY KEY to include this new column
ALTER TABLE `a4b9577448d6_noten`
ADD COLUMN `run_number` TINYINT(3) UNSIGNED NOT NULL DEFAULT 1 AFTER `jahr`,
DROP PRIMARY KEY,
ADD PRIMARY KEY (`person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`, `run_number`);
-- 2. Update the configuration table to support program-specific run counts
ALTER TABLE `a4b9577448d6_noten_bezeichnungen`
ADD COLUMN `anzahl_laeufe_json` TEXT NULL DEFAULT NULL AFTER `pro_geraet`;

View File

@@ -0,0 +1,68 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
// ---------- Get and sanitize input ----------
$type = isset($_GET['type']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_GET['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($_GET['ctext']) ? $_GET['ctext'] : '';
}
$folder = realpath($baseDir.'/displays/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;

View File

@@ -0,0 +1,89 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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',
'displayColourLogo',
'displayTextColourLogo',
'displayColorScoringBg',
'displayColorScoringBgSoft',
'displayColorScoringPanel',
'displayColorScoringPanelSoft',
'displayColorScoringPanelText',
'displayColorScoringPanelTextSoft',
'displayColorScoringPanelTextNoteL',
'displayColorScoringPanelTextNoteR',
'displayIdNoteL',
'displayIdNoteR',
'rechnungenName',
'rechnungenVorname',
'rechnungenStrasse',
'rechnungenHausnummer',
'rechnungenPostleitzahl',
'rechnungenOrt',
'rechnungenIBAN',
'maxLengthMusic',
'linkWebseite',
'rangNote',
'orderBestRang'
];
$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 $tableVar (`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;

View File

@@ -0,0 +1,84 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_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;
}
$action = $_POST['action'] ?? '';
if ($action === 'add') {
$name = trim($_POST['name'] ?? '');
$start_index = intval($_POST['start_index'] ?? 0);
$color = trim($_POST['color'] ?? '#424242');
if (!$name) {
echo json_encode(['success' => false, 'message' => 'Name ist erforderlich.']);
exit;
}
$stmt = $mysqli->prepare("INSERT INTO $tableGeraete (name, start_index, color_kampfrichter) VALUES (?, ?, ?)");
$stmt->bind_param("sis", $name, $start_index, $color);
$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'] ?? '';
$allowedFields = ['name', 'start_index', 'color_kampfrichter'];
if ($id > 0 && in_array($field, $allowedFields)) {
if ($field === 'start_index') {
$value = intval($value);
}
$updated = db_update($mysqli, $tableGeraete, [$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, $tableGeraete, ['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.']);
}

View File

@@ -0,0 +1,261 @@
<?php
header('Content-Type: application/json');
ini_set("display_errors", 1);
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_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;
}
$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 $tableNotenBezeichnungen 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 $tableNotenBezeichnungen (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) {
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_in_tabelle', 'zeige_in_tabelle_mobile', 'zeige_in_tabelle_admin', 'zeige_auf_rangliste', 'nullstellen', 'prefix_display'];
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, $tableNotenBezeichnungen, [$field => $value], ['id' => $id]);
if ($updated !== false) {
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, $tableNotenBezeichnungen, ['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.']);
exit;
}
if ($recalculateJSONs) {
$noten = db_select($mysqli, $tableNotenBezeichnungen, "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;
}
require $baseDir . "/../scripts/string-calculator/string-calculator-functions.php";
$notenRechner = new NotenRechner();
// 1. Build the direct map
// Format: [ Changed_Note_ID => [ "CalcId|GeraetId" => [CalcId, GeraetId] ] ]
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArray = $notenRechner->getBenoetigteIdsComplex($calc['berechnung']);
if (empty($neededIdsArray)) {
continue;
}
$calcId = (int)$calc['id'];
foreach ($neededIdsArray as $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;
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
// Store it as the "little array" you requested: [DependentCalcId, GeraetId]
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId];
}
}
// 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 $tableNotenBezeichnungen SET `berechnung_json` = NULL";
$mysqli->query($resetSql);
// Step 2: Prepare the statement
$updateSql = "UPDATE $tableNotenBezeichnungen 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;
}

View File

@@ -0,0 +1,204 @@
<?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');
session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
$baseDir = $_SERVER['DOCUMENT_ROOT'];
// 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
]);

View File

@@ -0,0 +1,321 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
error_reporting(E_ALL);
session_start();
if (
empty($_SESSION['access_granted_kampfrichter']) ||
$_SESSION['access_granted_kampfrichter'] !== true ||
empty($_SESSION['passcodekampfrichter_id']) ||
intval($_SESSION['passcodekampfrichter_id']) < 1
) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/csrf_functions.php';
if (!verify_csrf()) {
echo json_encode(['success' => false, 'message' => 'Forbidden']);
exit;
}
// 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;
}
$isAdmin = (($_SESSION['selectedFreigabeKampfrichter'] ?? '') === 'admin') ? true : false;
$disciplines = db_select($mysqli, $tableGeraete, 'id', '', [], 'start_index ASC');
$disciplines = array_column($disciplines, "id");
if (!$isAdmin) {
$discipline = intval($_POST['geraet']) ?? 0;
if (!in_array($discipline, $disciplines)) {
echo json_encode(['success' => false, 'message' => 'Falsche Geräte ID']);
exit;
}
$disciplines = [$discipline];
$stmt = $mysqli->prepare("
SELECT
t.name,
t.vorname,
t.programm,
p.id as programm_id,
agg.abteilung,
agg.geraeteIndex,
agg.startIndex
FROM $tableTurnerinnen t
LEFT JOIN $tableProgramme 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 $tableTurnerinnenAbt ta
INNER JOIN $tableAbt a
ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete g
ON g.id = ta.geraet_id
GROUP BY ta.turnerin_id
) agg ON agg.turnerin_id = t.id
WHERE t.id = ?
");
} else {
$stmt = $mysqli->prepare("SELECT t.`name`, t.`vorname`, t.`programm`, p.id as programm_id FROM $tableTurnerinnen t LEFT JOIN $tableProgramme p ON p.programm = t.programm 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;
}
$now = new DateTime();
$jahr = ($now->format('n') > 6) ? $now->modify('+1 year')->format('Y') : $now->format('Y');
if ($isAdmin) {
$stmt = $mysqli->prepare("SELECT `note_bezeichnung_id`, `value`, `geraet_id`, `run_number` FROM $tableNoten WHERE `person_id` = ? AND `jahr` = ?");
$stmt->bind_param('ss', $editId, $jahr);
} else {
$stmt = $mysqli->prepare("SELECT `note_bezeichnung_id`, `value`, `geraet_id`, `run_number` FROM $tableNoten WHERE `person_id` = ? AND `geraet_id` = ? AND `jahr` = ?");
$stmt->bind_param('sss', $editId, $discipline, $jahr);
}
$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 $tableNotenBezeichnungen");
$stmt->execute();
$result = $stmt->get_result();
$notenConfig = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$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[$programm_id] ?? $anzRunsConfig['default'] ?? 1;
for ($r = 1; $r <= $runs; $r++) {
$value = $indexedNotenDB[$d][$snC['id']][$r] ?? $snC['default_value'] ?? 0;
$noten[$d][$snC['id']][$r] = number_format($value, $snC['nullstellen'] ?? 2);
}
}
}
$titel = $row['vorname'].' '.$row['name'].', '.$row['programm'];
if (!$isAdmin) {
// $entries = db_select($mysqli, $tableTurnerinnen, 'name, vorname, programm, id', 'abteilung = ? AND startgeraet = ?', [$row['abteilung'], $row['startgeraet']]);
$stmt = $mysqli->prepare("
SELECT
t.name,
t.vorname,
t.programm,
t.id,
agg.abteilung,
agg.geraeteIndex,
agg.startIndex
FROM $tableTurnerinnen 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 $tableTurnerinnenAbt ta
INNER JOIN $tableAbt a
ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete 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 = 2;
$bezahltoverride = 5;
$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
]);
} else {
echo json_encode([
'success' => true,
'id' => $editId,
'programm_id' => $programm_id,
'titel' => $titel,
'noten' => $noten,
'nturnerin' => $nturnerin
]);
}

View File

@@ -0,0 +1,327 @@
<?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);*/
// Start session if not already started
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// Check access
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true ||
empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
// Validate POST input
if (!isset($_POST['abteilung'])) {
http_response_code(400);
exit;
}
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-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$abteilung = trim($_POST['abteilung']);
$disciplines = db_select($mysqli, $tableGeraete, '*', '', [], 'start_index ASC');
// Load TCPDF
require $baseDir . '/wp-content/uploads/TCPDF-main/tcpdf.php';
$current_year = (date('n') > 6) ? date('Y') + 1 : date('Y');
class MYPDF extends TCPDF
{
public $current_year;
public $abteilung;
// Page header
public function Header()
{
$image_file = 'https://kutu-tage-beider-basel.testseite-fh-ht.ch/wp-content/uploads/2025/06/ktbb-logo.png';
$this->SetY(15);
$this->SetFont('helvetica', 'B', 20);
$this->Cell(0, 0, 'Protokoll Kutu-Tage beider Basel ' . $this->current_year . ' Abt. ' . $this->abteilung, 0, false, 'L', 0, '', 0, false, 'M', 'M');
$this->Image($image_file, 272, 5, 15, '', 'PNG', '', 'T', false, 300, '', false, false, 0, false, false, false);
}
// Page footer
public function Footer()
{
$this->SetY(-15);
$this->SetFont('helvetica', 'I', 8);
$this->Cell(0, 10, 'Seite ' . $this->getAliasNumPage() . ' von ' . $this->getAliasNbPages(), 0, false, 'C', 0, '', 0, false, 'T', 'M');
}
}
$pdf = new MYPDF('L', 'mm', 'A4', true, 'UTF-8', false);
//$pdf->AddFont('outfit-bold', '', 'outfitb.php');
$pdf->current_year = $current_year;
$pdf->abteilung = $abteilung;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('Turnerinnen System');
$pdf->SetTitle("KTBB_Protokoll_Abt." . $abteilung . "_" . $current_year . ".pdf");
$pdf->SetMargins(10, 20, 10);
$pdf->SetAutoPageBreak(TRUE, 10);
$pdf->SetFont('helvetica', '', 9);
$startindex = 0;
foreach ($disciplines as $singledisciplin){
$startindex ++;
$name_kampfrichterin1 = 'Nicht eingetragen';
$name_kampfrichterin2 = 'Nicht eingetragen';
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT * FROM $tableKrProtokoll
WHERE abteilung = ?
AND geraet = ?
");
$stmt->bind_param('ss', $abteilung, $singledisciplin['name']);
$stmt->execute();
$result = $stmt->get_result();
$krresults = $result->fetch_all(MYSQLI_ASSOC);
foreach ($krresults as $krresult){
if ($krresult['aufgabe'] == 1){
$name_kampfrichterin1 = $krresult['name'];
} elseif ($krresult['aufgabe'] == 2){
$name_kampfrichterin2 = $krresult['name'];
}
}
foreach ($disciplines as $shiftstartindex => $subdisciplin){
$pdf->AddPage();
$pdf->Ln(10);
$pdf->SetFont('', 'B', 14);
$pdf->Cell(0, 0, ucfirst($singledisciplin['name']), 0, 1, 'L');
$pdf->Ln(3);
$pdf->SetFont('', 'I', 9);
$newshiftindex = $shiftstartindex + 1;
$pdf->Cell(0, 0, 'Gruppe '. $newshiftindex , 0, 1, 'L');
$pdf->SetFont('', '');
$pdf->Ln(5);
$pdf->Cell(0, 0, '1. Kampfrichterin: ' . $name_kampfrichterin1, 0, 1, 'L');
$pdf->Ln(2);
$pdf->Cell(0, 0, '2. Kampfrichterin: ' . $name_kampfrichterin2, 0, 1, 'L');
$pdf->Ln(8);
$startg = $shiftstartindex - $startindex;
if ($startg < 1) {
$startg += 4; // shift into positive range
}
// Prepare SQL statement
$stmt = $mysqli->prepare("
SELECT
t.*,
agg.abteilung,
agg.geraete_index,
agg.start_index
FROM $tableTurnerinnen t
LEFT JOIN (
SELECT
ta.turnerin_id,
GROUP_CONCAT(DISTINCT a.name SEPARATOR ', ') AS abteilung,
GROUP_CONCAT(DISTINCT g.start_index SEPARATOR ', ') AS geraete_index,
ta.turnerin_index AS start_index
FROM $tableTurnerinnenAbt ta
INNER JOIN $tableAbt a
ON a.id = ta.abteilung_id
LEFT JOIN $tableGeraete g
ON g.id = ta.geraet_id
GROUP BY ta.turnerin_id
) agg ON agg.turnerin_id = t.id
WHERE (t.bezahlt = ? OR t.bezahltoverride = ?) AND agg.abteilung = ? AND agg.geraete_index = ?
ORDER BY agg.start_index DESC
");
$bezahlt1 = '2';
$bezahlt2 = '5';
$stmt->bind_param('ssss', $bezahlt1, $bezahlt2, $abteilung, $startg);
$stmt->execute();
$result = $stmt->get_result();
$entries = $result->fetch_all(MYSQLI_ASSOC);
if (!empty($entries)) {
$maxindex = $entries[0]['start_index'];
// Recalculate
foreach ($entries as &$singleorderentry) {
$singleorderentry['start_index'] -= $shiftstartindex;
if ($singleorderentry['start_index'] < 1) {
$singleorderentry['start_index'] += $maxindex;
}
}
unset($singleorderentry); // break the reference
// Reorder by recalculated startindex (ascending)
usort($entries, function($a, $b) {
return $a['start_index'] <=> $b['start_index'];
});
}
// Define columns (including discipline sub-columns)
$columns = [
'start_index' => ['header' => 'Str.'],
'name' => ['header' => 'Name'],
'vorname' => ['header' => 'Vorname'],
'programm' => ['header' => 'Programm'],
'geburtsdatum' => ['header' => 'Jg.'],
'verein' => ['header' => 'Verein'],
'e1 note '.strtolower($subdisciplin['name']) => ['header' => 'E1 Note '.ucfirst($subdisciplin['name'])],
'e2 note '.strtolower($subdisciplin['name']) => ['header' => 'E2 Note '.ucfirst($subdisciplin['name'])],
'e note '.strtolower($subdisciplin['name']) => ['header' => 'E Note '.ucfirst($subdisciplin['name'])],
'd-note '.strtolower($subdisciplin['name']) => ['header' => 'D Note '.ucfirst($subdisciplin['name'])],
'neutrale abzuege '.strtolower($subdisciplin['name']) => ['header' => 'N. Abzüge '.ucfirst($subdisciplin['name'])],
'note '.strtolower($subdisciplin['name']) => ['header' => 'Note '.ucfirst($subdisciplin['name'])],
];
// Initialize max widths with header widths (+4 mm padding)
foreach ($columns as $key => &$col) {
$col['max_width'] = $pdf->GetStringWidth($col['header']) + 4;
}
// Calculate max width of each column based on all data
foreach ($entries as $r) {
foreach ($columns as $key => &$col) {
isset($r[$key]) ? $r[$key] : '';
$text = '';
// Handle different column types
if ($key === 'start_index' && !empty($r['start_index'])) {
$text = (int)$r[$key];
} elseif ($key === 'geburtsdatum' && !empty($r['geburtsdatum'])) {
$text = (new DateTime($r['geburtsdatum']))->format('Y');
} elseif (strpos($key, 'd-note') === 0 && isset($r[$key]) && is_numeric($r[$key])) {
$text = number_format((float)$r[$key], 2, '.', ''); // 2 decimals
} elseif (isset($r[$key]) && is_numeric($r[$key])) {
$text = number_format((float)$r[$key], 3, '.', ''); // 3 decimals
} elseif (isset($r[$key])) {
$text = (string)$r[$key];
} else {
$text = '';
}
// Calculate width with padding
$w = $pdf->GetStringWidth($text) + 4;
// Update max width
if ($w > $col['max_width']) {
$col['max_width'] = $w;
}
}
}
unset($col);
$columns['geburtsdatum']['max_width'] = $pdf->GetStringWidth("0000") + 4;
// Print first header row
$pdf->SetFont('', 'B');
foreach ($columns as $key => $col) {
if (
$key === 'd-note' || // only column with dash
strpos($key, 'note') === 0 || // matches 'note...' at start
strpos($key, 'e note') === 0 || // matches 'e-note...'
strpos($key, 'e1 note') === 0 || // matches 'e1 note ...'
strpos($key, 'e2 note') === 0 || // matches 'e2 note ...'
strpos($key, 'neutrale abzuege') === 0 ||
$key === 'geburtsdatum' ||
$key === 'start_index'
) {
$align = 'C';
} else {
$align = 'L';
}
$pdf->Cell($col['max_width'], 7, $col['header'], 0, 0, $align);
}
// Move to next line after headers
$pdf->Ln();
$pdf->SetFont('', '');
foreach ($entries as $r) {
foreach ($columns as $key => $col) {
// Determine alignment
if (strpos($key, 'note') === 0 || strpos($key, 'e note') === 0 || strpos($key, 'd-note') === 0 || strpos($key, 'neutrale abzuege') === 0 || $key === 'start_index' || $key === 'geburtsdatum' || strpos($key, 'e1 note') === 0 || strpos($key, 'e2 note') === 0){
$align = 'C';
} else {
$align = 'L';
}
// Format the value based on type
if ($key === 'geburtsdatum' && !empty($r['geburtsdatum'])) {
$text = (new DateTime($r['geburtsdatum']))->format('Y');
} elseif (strpos($key, 'note') === 0 || strpos($key, 'e note') === 0 || strpos($key, 'd-note') === 0 || strpos($key, 'neutrale abzuege') === 0 || $key === 'gesamt' || strpos($key, 'e1 note') === 0 || strpos($key, 'e2 note') === 0){
$text = isset($r[$key]) ? number_format((float)$r[$key], 3) : '';
} else {
$text = isset($r[$key]) ? $r[$key] : '';
}
$pdf->Cell($col['max_width'], 6, $text, 1, 0, $align);
}
$pdf->Ln(); // Move to next row
}
$pdf->SetY(-50);
if ($name_kampfrichterin1 !== 'Nicht eingetragen' || $name_kampfrichterin2 !== 'Nicht eingetragen'){
$pdf->SetFont('', 'B');
$pdf->Cell(0, 28, 'Unterschrift:', 0, 1, 'L');
$pdf->SetFont('', '');
if ($name_kampfrichterin1 !== 'Nicht eingetragen'){
$pdf->Cell(120, 8, $name_kampfrichterin1.': ___________________________________________', 0, 0, 'L');
}
if ($name_kampfrichterin2 !== 'Nicht eingetragen'){
$pdf->Cell(90, 8, $name_kampfrichterin2.': ___________________________________________', 0, 0, 'L');
}
}
}
}
// Close statement
$stmt->close();
$pdf->Output("Protokoll_Kutu-Tage_beider_Basel_{$current_year}_Abt_{$abteilung}.pdf", 'D');
exit;

View File

@@ -0,0 +1,606 @@
<?php
use TCPDF;
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);
// Start session if not already started
if (session_status() !== PHP_SESSION_ACTIVE) {
session_start();
}
// Check access
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true ||
empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
// Validate POST input
/*
if (!isset($_POST['prog']) && !isset($_POST['type'])) {
http_response_code(400);
exit;
}*/
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';
$programm = trim($_POST['prog'] ?? 'p6a');
$buttontype = trim($_POST['type'] ?? 'export_programm');
$upperprogramm = strtoupper($programm);
$stmt = $mysqli->prepare("SELECT `id`, `name` FROM $tableGeraete 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 $tableTurnerinnen
WHERE LOWER(programm) = LOWER(?)
AND (bezahlt = ? OR bezahltoverride = ?)
ORDER BY rang ASC
");
$bezahlt1 = '2';
$bezahlt2 = '5';
$stmt->bind_param('sss', $programm, $bezahlt1, $bezahlt2);
$stmt->execute();
$result = $stmt->get_result();
$personen = $result->fetch_all(MYSQLI_ASSOC);
// Close statement
$stmt->close();
// 1. Get the IDs from the first query results
$turnerinnenIds = array_column($personen, 'id');
if (!empty($turnerinnenIds)) {
// 2. Create a string of placeholders: ?,?,?
$placeholders = implode(',', array_fill(0, count($turnerinnenIds), '?'));
// 3. Prepare the IN statement
$sqlNoten = "SELECT * FROM $tableNoten WHERE person_id IN ($placeholders) AND `jahr` = ?";
$stmtNoten = $mysqli->prepare($sqlNoten);
// --- FIX STARTS HERE ---
// 1. Combine all values into one flat array for binding
$paramsArray = array_merge($turnerinnenIds, [$current_year]);
// 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 = [];
}
$rangNote = intval(db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['rangNote']));
var_dump($rangNote);
$orderBestRang = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['orderBestRang']);
$okValuesOrderBestRang = ["ASC", "DESC"];
if (!in_array($orderBestRang, $okValuesOrderBestRang)) {
http_response_code(400);
exit;
}
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, default_value, nullstellen, pro_geraet, geraete_json, zeige_auf_rangliste, name", "zeige_auf_rangliste = ? OR id = ?", ['1', $rangNote]);
$displayedNoten = [];
foreach ($alleNoten as $sN) {
if (intval($sN['zeige_auf_rangliste']) === 1) {
$displayedNoten[$sN['id']] = $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 = [];
foreach ($personen as $sp) {
$pId = $sp['id'];
foreach ($geraete as $g) {
$indexedNotenArray[$pId][$g['id']] = [];
}
$indexedNotenArray[$pId][0] = []; // Total/Overall device per person
}
// 2. Fill with existing database values
$sortArray = [];
foreach ($notenEntries as $sn) {
$pId = $sn['person_id'];
$gId = $sn['geraet_id'];
$nId = $sn['note_bezeichnung_id'];
$val = $sn['value'];
$indexedNotenArray[$pId][$gId][$nId] = $val;
// Check if this specific note is the one we rank by
// Usually ranking happens on Device 0 (Total) for the $rangNote ID
if (intval($nId) === $rangNote && intval($gId) === 0) {
$sortArray[$pId] = $val; // FIXED: used = instead of ===
}
}
// 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 MYPDF extends TCPDF
{
public $current_year;
public $programm;
public $upperprogramm;
public $columns;
public $disciplines;
// Page header
public $headerBottomY = 0;
public function Header()
{
$image_file = 'https://kutu-tage-beider-basel.testseite-fh-ht.chhttps://kutu-tage-beider-basel.testseite-fh-ht.ch/wp-content/uploads/2025/06/ktbb-logo.png';
$this->SetY(15);
$this->SetFont('helvetica', 'B', 20);
$this->Cell(0, 0, 'Rangliste Kutu-Tage beider Basel ' . $this->current_year, 0, 1, 'L', 0, '', 0, false, 'M', 'M');
$preimageX = $this->GetX();
$preimageY = $this->GetY();
$this->Image($image_file, 272, 5, 15, '', 'JPG', '', 'T', false, 300, '', false, false, 0, false, false, false);
$this->SetX($preimageX);
$this->SetY($preimageY);
$this->SetFont('helvetica', '', 11);
$this->Cell(0, 10, 'Programm: ' . $this->upperprogramm, 0, 1, 'L');
$this->Ln(5);
$columns = $this->columns;
$disciplines = $this->disciplines;
// Print first header row
$preheaderX = $this->GetX();
$preheaderY = $this->GetY();
$this->SetFont('', 'B');
$this->Cell($columns['rang']['max_width'], 7, $columns['rang']['header'], 0, 0, 'C');
$this->Cell($columns['name']['max_width'], 7, $columns['name']['header'], 0, 0, 'L');
$this->Cell($columns['vorname']['max_width'], 7, $columns['vorname']['header'], 0, 0, 'L');
$this->Cell($columns['geburtsdatum']['max_width'], 7, $columns['geburtsdatum']['header'], 0, 0, 'C');
$this->Cell($columns['verein']['max_width'], 7, $columns['verein']['header'], 0, 0, 'L');
$startX = $this->GetX();
$startY = $this->GetY();
$this->SetLineWidth(0.2);
// Loop through disciplines for rotated headers
foreach ($disciplines as $d) {
$w = $columns["d-note $d"]['max_width'] + $columns["note $d"]['max_width'];
$ws = $columns["note $d"]['max_width'];
$x = $this->GetX();
$y = $this->GetY() + 7;
// Start transformation for rotation
$this->StartTransform();
// Rotate around top-left of cell
$this->Rotate(45, $x, $y);
// Draw a line **above the first data row**
$lineY = $startY + 7; // adjust 7 depending on cell height
$this->Line($x, $lineY, $x + $ws, $lineY);
// Print the rotated header text
$this->Cell($w, 7, ucfirst($d), 0, 0, 'L');
$this->StopTransform();
$this->SetX($x + $w);
}
$w = $columns["gesamt"]['max_width'];
$ws = $w;
$x = $this->GetX();
$y = $this->GetY() + 7;
// Start transformation for rotation
$this->StartTransform();
// Rotate around top-left of cell
$this->Rotate(45, $x, $y);
// Draw a line **above the first data row**
$lineY = $startY + 7; // adjust 7 depending on cell height
$this->SetLineWidth(0.6);
$this->Line($x, $lineY, $x + $ws, $lineY);
$this->SetLineWidth(0.2);
// Print the rotated header text
$this->Cell($w, 7, $columns['gesamt']['header'], 0, 0, 'L');
$this->StopTransform();
$this->SetX($x + $w);
$afterheaderX = $this->GetX();
$this->Ln();
$xheaderline = $preheaderX;
$yheaderline = $this->GetY();
$this->headerBottomY = $this->GetY();
}
// Page footer
public function Footer()
{
$this->SetY(-15);
$this->SetFont('helvetica', '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, $_SERVER['HTTP_HOST'], 0, 0, 'R', false, 'https://'.$_SERVER['HTTP_HOST'].'/ergebnis/'.$this->current_year, 0, false, 'T', 'M');
$this->SetLineWidth(0.6);
$this->Line(10, $this->headerBottomY, 297 - 10, $this->headerBottomY);
$this->SetLineWidth(0.2);
$this->SetY(0);
$this->SetX(5);
$this->SetFillColor(255, 255, 255); // white
$this->Cell(5, 190, '', 0, 0, 'L', true);
$this->SetY(0);
$this->SetX(297 - 10);
$this->Cell(5, 190, '', 0, 0, 'L', true);
}
}
$pdf = new MYPDF('L', 'mm', 'A4', true, 'UTF-8', false);
$pdf->current_year = $current_year;
$pdf->programm = $programm;
$pdf->upperprogramm = $upperprogramm;
$pdf->SetCreator(PDF_CREATOR);
$pdf->SetAuthor('WKVS-FH');
$pdf->SetTitle("KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf");
$pdf->SetAutoPageBreak(TRUE, 10);
$pdf->SetFont('helvetica', '', 11);
// Define columns (including discipline sub-columns)
$columns = [
'rang' => [ 0 => ['header' => 'Rang']],
'name' => [ 0 => ['header' => 'Name']],
'vorname' => [ 0 => ['header' => 'Vorname']],
'geburtsdatum' => [ 0 => ['header' => 'Jg.']],
'verein' => [ 0 => ['header' => 'Verein']],
];
foreach ($displayedNoten as $sdN) {
if (intval($sdN['pro_geraet']) === 1) {
foreach ($geraete as $g) {
$columns[$sdN['name']][$g['id']] = ['header' => $sdN['name'], 'nullstellen' => $sdN['nullstellen'] ?? 2];
}
} else {
$allowed = $ascArrayGeraeteJSON[$sdN['id']] ?? [];
foreach ($allowed as $gId) {
$columns[$sdN['name']][$gId] = ['header' => $sdN['name'], 'nullstellen' => $sdN['nullstellen'] ?? 2];
}
}
}
// Initialize max widths with header widths (+4 mm padding)
foreach ($columns as $key => $col) {
foreach ($col as $skey => $scol) {
$columns[$key][$skey]['max_width'] = $pdf->GetStringWidth($scol['header']) + 8;
}
}
// 3. Fill missing defaults and finalize the sortArray
foreach ($personen as $sp) {
$pId = $sp['id'];
// We check all possible devices (including 0)
foreach ($indexedNotenArray[$pId] as $sG => $currentNoten) {
foreach ($alleNoten as $noteDef) {
$neni = $noteDef['id'];
// Skip if note already exists for this person/device
if (isset($currentNoten[$neni])) continue;
// 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;
}
// Assign default value
$defaultValue = $ascArrayDefaultValues[$neni] ?? 0;
$string = nuber_format($defaultValue, $noteDef['nullstellen']);
$indexedNotenArray[$pId][$sG][$neni] = $string;
$width = $pdf->GetStringWidth($string) + 4;
if ($w > $columns[$noteDef['name']][$sG]['max_width']) {
$columns[$noteDef['name']][$sG] = $w;
}
// If this missing note was our ranking note, add default to sortArray
if ($neni === $rangNote && $sG === 0) {
$sortArray[$pId] = $defaultValue;
}
}
}
}
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;
}
// 4. Calculate Ranks
// FIXED: Passing $orderBestRang (the string "ASC"/"DESC") instead of the array
$ranks = calculateRanks($sortArray, $orderBestRang);
// Calculate max width of each column based on all data
foreach ($personen as $p) {
foreach ($columns as $key => &$col) {
if (isset($p[$key])) {
$text = $p[$key];
} else {
$text = '';
}
$w = $pdf->GetStringWidth($text) + 4;
// Add some padding
if ($w > $col['max_width']) {
$col['max_width'] = $w;
}
}
}
unset($col);
$columns['geburtsdatum'][0]['max_width'] = $pdf->GetStringWidth("0000") + 8;
$tablew = 297 - 20; // total table width (A4 landscape in mm)
// Step 1: Calculate total width of fixed columns
$fixedWidth = 0;
/*
// Discipline columns (each discipline has two columns: "d-note" and "note")
foreach ($disciplines as $d) {
$fixedWidth += $columns["d-note $d"]['max_width'] + $columns["note $d"]['max_width'];
}
// Other fixed columns
$fixedColumns = ['geburtsdatum','rang','gesamt'];
foreach ($fixedColumns as $col) {
$fixedWidth += $columns[$col]['max_width'];
}
// Step 2: Compute extra space for flexible columns
$flexCols = ['name','vorname','verein']; // columns that can grow
$flexTotal = 0;
// Compute current total width of flexible columns
foreach ($flexCols as $col) {
$flexTotal += $columns[$col]['max_width'];
}
// Available width for flexible columns
$effTableWidth = $tablew - $fixedWidth;
$extraWidth = $effTableWidth - $flexTotal;
// Step 3: Distribute extra width proportionally among flexible columns
if ($extraWidth > 0) {
foreach ($flexCols as $col) {
$columns[$col]['max_width'] += ($columns[$col]['max_width'] / $flexTotal) * $extraWidth;
}
}
*/
$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(10, $margin_top, 10); // +5 mm padding below header
$pdf->SetY($margin_top); // Move cursor below header manually
$mbs = false;
$mbl = false;
$pdf->SetFont('', '');
$index = 0;
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 ($index % 2 == 0) {
$pdf->SetFillColor(255, 255, 255); // white
} else {
$pdf->SetFillColor(240, 240, 240); // light gray
}
$pdf->SetFont('', 'B');
/*
if ($buttontype === 'export_programm_bm') {
if (($r['verein'] === 'BTV Basel' || $r['verein'] === 'TV Basel') && $mbs === false){
$pdf->SetFillColor(0, 0, 0);
$pdf->SetTextColor(255, 255, 255);
$mbs = true;
} elseif (($r['verein'] === 'Kutu Regio Basel') && $mbl === false) {
$pdf->SetFillColor(255, 0, 0);
$pdf->SetTextColor(255, 255, 255);
$mbl = true;
}
}*/
$pdf->Cell($columns['rang'][0]['max_width'], 8, $rang, 'B', 0, 'C', true);
$pdf->SetFont('', '');
$pdf->Cell($columns['name'][0]['max_width'], 8, $r['name'], 'B', 0, 'L', true);
$pdf->Cell($columns['vorname'][0]['max_width'], 8, $r['vorname'], 'B', 0, 'L', true);
$pdf->Cell($columns['geburtsdatum'][0]['max_width'], 8, (new DateTime($r['geburtsdatum']))->format("Y"), 'B', 0, 'C', true);
$pdf->Cell($columns['verein'][0]['max_width'], 8, $r['verein'], 'B', 0, 'L', true);
foreach ($disciplines as $d) {
$d_note = isset($r["d-note $d"]) ?
number_format((float)$r["d-note $d"], 2) : '';
$note = isset($r["note $d"]) ? number_format((float)$r["note $d"], 3) : '';
$pdf->SetFont('', '', 9);
$pdf->Cell($columns["d-note $d"]['max_width'], 8, $d_note, 'LB', 0, 'C', true);
$pdf->SetFont('', '', 11);
$pdf->Cell($columns["note $d"]['max_width'], 8, $note, 'B', 0, 'C', true);
}
$gesamt = isset($r["gesamtpunktzahl"]) ? number_format((float)$r["gesamtpunktzahl"], 3) : '';
$pdf->SetFont('', 'B');
$pdf->Cell($columns['gesamt'][0]['max_width'], 8, $gesamt, 'B', 0, 'C', true);
$pdf->SetFont('', '');
$x = $pdf->GetX() - $columns['gesamt'][0]['max_width'];
$y = $pdf->GetY();
$pdf->SetLineWidth(0.6);
$pdf->Line($x, $y, $x, $y + 7.7);
$pdf->SetLineWidth(0.2);
$pdf->Ln();
$index++;
}
$pdf->SetFont('', '', 7);
$textanzturnerinnen = (count($entries) > 1) ? 'Turnerinnen': 'Turnerin';
$pdf->Cell(20, 6, count($entries).' '.$textanzturnerinnen, 0, 0, 'L');
if ($buttontype === 'export_programm' || $buttontype === 'export_programm_bm') {
$pdf->Output("KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf", 'D');
exit;
} elseif ($buttontype === 'upload_programm') {
$dir = $baseDir . '/wp-content/ergebnisse/';
if (!file_exists($dir)) mkdir($dir, 0755, true);
$localPath = $dir . "KTBB_Ergebnisse_" . $programm . "_" . $current_year . ".pdf";
if (file_exists($localPath)) unlink($localPath);
$pdf->Output($localPath, 'F');
$_SESSION['form_message'] = '<div class="success">PDF wurde hochgeladen</div>';
exit;
}

View File

@@ -0,0 +1,107 @@
<?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);
header('Content-Type: application/json');
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';
// ---------- Get and sanitize input ----------
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$discipline = isset($_GET['discipline']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_GET['discipline']) : '';
if ($discipline !== 'boden') {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
exit;
}
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$result = $mysqli->query("SELECT t.bodenmusik, agg.filePath FROM `$tableTurnerinnen` t LEFT JOIN (SELECT a.id, a.file_path AS filePath FROM $tableAudiofiles a) agg ON agg.id = t.bodenmusik WHERE t.id = $id");
$row = $result->fetch_assoc();
if (!$row || !isset($row['bodenmusik'])) {
echo json_encode(['success' => false, 'message' => 'Row fetch failed '.$id]);
exit;
}
if ($row['bodenmusik'] === "0" || $row['filePath'] == null) {
echo json_encode(['success' => false, 'message' => 'Keine Musik']);
exit;
}
$folder = realpath($baseDir . '/displays/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'audio.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, []);
}
$jsonString = file_get_contents($filepath);
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
$oldjson["musik"] = $row['filePath'];
$oldjson["start"] = true;
$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 updated successfully for '.$discipline,
'disable_musik_button' => true
]);
exit;

View File

@@ -0,0 +1,57 @@
<?php
header('Content-Type: application/json');
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$discipline = 'boden';
$folder = realpath($baseDir . '/displays/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'audio.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, []);
}
$jsonString = file_get_contents($filepath);
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
$oldjson["start"] = false;
$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 updated successfully for '.$discipline,
'disable_musik_button' => true
]);
exit;

View File

@@ -0,0 +1,114 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', '1');
ini_set('display_startup_errors', '1');
error_reporting(E_ALL);
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
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';
$abteilung = isset($_POST['abteilung']) ? strval($_POST['abteilung']) : '1';
$aufgabe = isset($_POST['aufgabe']) ? strval($_POST['aufgabe']) : '2';
$name = isset($_POST['name']) ? strval($_POST['name']) : 'RK';
$geraet = isset($_POST['geraet']) ? strtolower($_POST['geraet']) : 'boden';
if ($abteilung === ''){
echo json_encode(['success' => false, 'message' => 'Keine Abteilung angegeben.']);
exit;
}
if ($aufgabe === ''){
echo json_encode(['success' => false, 'message' => 'Keine Aufgabe angegeben.']);
exit;
}
if (!is_numeric($aufgabe)){
echo json_encode(['success' => false, 'message' => 'Keine valide Aufgabe. (is not numeric)']);
exit;
}
if ($name === ''){
echo json_encode(['success' => false, 'message' => 'Kein Namen angegeben']);
exit;
}
$stmt = $mysqli->prepare("SELECT `name` FROM $tableGeraete ORDER BY start_index ASC");
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
$disciplines = array_map(
'strtolower',
array_column($result->fetch_all(MYSQLI_ASSOC), 'name')
);
$stmt->close();
if (!in_array($geraet, $disciplines)){
echo json_encode(['success' => false, 'message' => 'Invalides Gerät: '.$geraet]);
exit;
}
$stmt = $mysqli->prepare("SELECT * FROM $tableKrProtokoll WHERE abteilung = ? AND geraet = ? AND aufgabe = ? LIMIT 1");
$stmt->bind_param("sss", $abteilung, $geraet, $aufgabe);
$stmt->execute();
$result = $stmt->get_result();
$row = $result->fetch_assoc();
if ($row){
$updatestmt = $mysqli->prepare("UPDATE $tableKrProtokoll SET name = ? WHERE id = ?");
$updatestmt->bind_param("si", $name, $row['id']);
if (!$updatestmt->execute()) {
echo json_encode([
'success' => false,
'message' => 'Insert ERROR: ' . $updatestmt->error
]);
exit;
}
echo json_encode(['success' => true, 'message' => 'Updated: updated']);
exit;
} else {
$insertstmt = $mysqli->prepare("INSERT INTO $tableKrProtokoll (abteilung, geraet, name, aufgabe) VALUES (?, ?, ?, ?)");
$insertstmt->bind_param("ssss", $abteilung, $geraet, $name, $aufgabe);
if (!$insertstmt->execute()) {
echo json_encode([
'success' => false,
'message' => 'Insert ERROR: ' . $insertstmt->error
]);
exit;
}
echo json_encode(['success' => true, 'message' => 'Updated: inserted']);
exit;
}
?>

View File

@@ -0,0 +1,51 @@
<?php
session_start();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$new_value = $_POST['freigabe'] ?? '';
$type = $_POST['type'] ?? 'nan';
$allowedTypes = ['kampfrichter', 'trainer'];
if (in_array($type, $allowedTypes)) {
$accessKey = "access_granted_" . $type;
$idKey = "passcode" . $type . "_id";
// 3. Check if they have access
$hasAccess = isset($_SESSION[$accessKey]) &&
$_SESSION[$accessKey] === true &&
!empty($_SESSION[$idKey]) &&
$_SESSION[$idKey] > 0;
if (!$hasAccess) {
echo json_encode(['success' => false, 'message' => 'no permissions']);
exit;
}
} else {
echo json_encode(['success' => false, 'message' => 'no permissions']);
exit;
}
if (!$new_value) {
echo json_encode('Invalid Input');
exit;
}
if ($type === 'kampfrichter'){
$_SESSION['selectedFreigabeKampfrichter'] = $new_value;
}
if ($type === 'trainer'){
$_SESSION['selectedFreigabeTrainer'] = $new_value;
}
// ---------- Return JSON ----------
echo json_encode(['success' => true, 'message' => 'SESSION updated']);
exit;

View File

@@ -0,0 +1,252 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
header('Content-Type: application/json');
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-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 $tableGeraete 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, $tableNotenBezeichnungen, "*", "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;
}
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`, `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, $jahr, $run_number);
$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, $tableGeraete, "id");
// Alle Werte werden von der Datenbank geholt und werden, wenn nicht vorhanden, durch den Standartwert ersetzt.
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, berechnung, default_value, nullstellen, pro_geraet, geraete_json, anzahl_laeufe_json");
$noten = db_select($mysqli, $tableNoten, "`value`, `note_bezeichnung_id`, `geraet_id`, `run_number`", "`person_id` = ? AND `jahr` = ?", [$person_id, $jahr]);
$ascArrayDefaultValues = array_column($alleNoten, 'default_value', 'id');
$ascArrayProGeraet = array_column($alleNoten, 'pro_geraet', 'id');
$ascArrayGeraeteJSON = array_column($alleNoten, 'geraete_json', 'id');
$ascArrayAnzahlLaeufeJSON = array_column($alleNoten, 'anzahl_laeufe_json', 'id');
$ascArrayRechnungen = array_column($alleNoten, 'berechnung', 'id');
// $proGeraet = intval($calc['pro_geraet']) !== 1;
$indexedNotenArray = [];
foreach ($ascArrayGeraeteJSON as $key => $saagj) {
$ascArrayGeraeteJSON[$key] = json_decode($saagj, true);
}
foreach ($ascArrayAnzahlLaeufeJSON as $key => $saalj) {
$ascArrayAnzahlLaeufeJSON[$key] = json_decode($saalj, true) ?? [];
}
foreach ($geraete as $g) {
$indexedNotenArray[$g['id']] = [];
}
$indexedNotenArray[0] = [];
foreach ($noten as $sn) {
$indexedNotenArray[$sn['geraet_id']][$sn['note_bezeichnung_id']][$sn['run_number']] = $sn['value'];
}
$alleNotenIds = array_column($alleNoten, 'id') ?? [];
foreach ($indexedNotenArray as $sG => $siNA) {
$existierendeNotenIds = array_keys($siNA) ?? [];
$nichtExistierendeNotenIds = array_diff($alleNotenIds, $existierendeNotenIds) ?? [];
foreach ($nichtExistierendeNotenIds as $neni) {
if (!isset($ascArrayDefaultValues[$neni])) { continue; }
if (intval($ascArrayProGeraet[$neni]) === 1 && intval($sG) === 0) { continue; }
if (intval($ascArrayProGeraet[$neni]) !== 1 && (!is_array($ascArrayGeraeteJSON[$neni]) || !in_array($sG, $ascArrayGeraeteJSON[$neni]))) { continue; }
// For non-existent notes, we fill all runs with default value
// We set Run 1 by default, and if more are configured, also those
$indexedNotenArray[$sG][$neni][1] = $ascArrayDefaultValues[$neni];
// Check for more runs in config? (Actually, this might be overkill for defaults,
// but the calculator might need them)
}
}
// We only want to save the IDs that were actually recalculated
$idsToSave = [];
foreach ($abhaenigeRechnungen as $sRechnung) {
if ($sRechnung[1] !== "A" && intval($sRechnung[1]) !== $gereat_id) { continue; }
$rechnung = $ascArrayRechnungen[$sRechnung[0]] ?? null;
$gereadIdArrays = ($sRechnung[1] === "A") ? $gereat_id : $sRechnung[1];
$targetNoteId = $sRechnung[0];
$isProGeraet = (intval($ascArrayProGeraet[$targetNoteId]) === 1);
$allowedGeraete = $ascArrayGeraeteJSON[$targetNoteId] ?? [];
if ($isProGeraet) {
$gereadIdArrays = $gereat_id;
} elseif (in_array($gereat_id, $allowedGeraete)) {
$gereadIdArrays = $gereat_id;
} else {
$gereadIdArrays = 0;
}
if ($rechnung === null) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, Fehler: Rechnung" . $sRechnung[0] . "nicht gefunden"]);
exit;
}
$calcResult = $notenRechner->berechneStringComplex($rechnung, $indexedNotenArray, $gereat_id);
if ($calcResult['success'] !== true) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, Rechenfehler: " . ($calcResult['value'] ?? '')]);
exit;
}
// Update the local array (Always Run 1 for calculations for now, UNLESS we want calculated runs?)
// Most calculations are "Total" points which have run_number = 1
$indexedNotenArray[$gereadIdArrays][$sRechnung[0]][1] = $calcResult['value'];
// Track that this ID needs to be written to the database (Target run is 1)
$updatedValues[$gereadIdArrays][$sRechnung[0]][1] = $calcResult['value'];
}
// Prepare the statement once
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`, `run_number`)
VALUES (?, ?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
$indexedNullstellen = array_column($alleNoten, 'nullstellen', 'id');
$formatedNoten = [];
foreach ($updatedValues as $gereat => $notenArray) {
foreach ($notenArray as $note => $runArray) {
foreach ($runArray as $run => $value) {
$stmt->bind_param("siiiii", $value, $person_id, $note, $gereat, $jahr, $run);
$stmt->execute();
$formatedNoten[$gereat][$note][$run] = number_format($value ,$indexedNullstellen[$note] ?? 2);
}
}
}
$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
]);

View File

@@ -0,0 +1,93 @@
<?php
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1 || !isset($_SESSION['selectedFreigabeKampfrichter']) || $_SESSION['selectedFreigabeKampfrichter'] !== 'admin') {
http_response_code(403);
exit;
}
header('Content-Type: application/json');
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';
// ---------- Get and sanitize input ----------
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$fieldType = isset($_POST['field_type']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_POST['field_type']) : '';
$discipline = isset($_POST['discipline']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_POST['discipline']) : '';
$value = isset($_POST['value']) ? floatval($_POST['value']) : 0;
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
if (!isset($value) || floatval($value) < 0 || !isset($discipline) || $discipline === ''|| !isset($fieldType) || $fieldType === '') {
http_response_code(422);
exit;
}
if ($discipline === 'all') {
$column = $fieldType;
} else {
$column = $fieldType . ' ' . $discipline;
}
$excluded_columns = [
'id',
'name',
'vorname',
'bezahlt',
'bezahltoverride',
'geburtsdatum',
'programm',
'verein',
'bodenmusik'
];
$sql = "SHOW COLUMNS FROM `$tableTurnerinnen`";
$result = $mysqli->query($sql);
$all_columns = [];
while ($row = $result->fetch_assoc()) {
$all_columns[] = $row['Field'];
}
$allowed_columns = array_values(
array_diff($all_columns, $excluded_columns)
);
if (!in_array($column, $allowed_columns, true)) {
http_response_code(422);
exit;
}
$stmt = $mysqli->prepare("UPDATE `$tableTurnerinnen` SET `$column` = ? WHERE id = ?");
$stmt->bind_param("di", $value, $id);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$stmt->close();
$mysqli->close();
http_response_code(200);
echo json_encode([
'success' => true,
'message' => 'Updated successfully'
]);
exit;

View File

@@ -0,0 +1,216 @@
<?php
header('Content-Type: application/json');
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
// ---------- Get and sanitize input ----------
$id = intval($_POST['personId']) ?? 0;
$geraetId = intval($_POST['geraetId']) ?? 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 $tableGeraete 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 . '/displays/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'display_' . strtolower($geraetName) . '.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 * FROM `$tableTurnerinnen` 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];
// safely get value, default 0 if missing
$olduniqueid = $oldjson['uniqueid'] ?? 0;
$uniqueid = $olduniqueid + 1;
$data = ["noteLinks" => '',
"noteRechts" => '',
"id" => $id,
"name" => $row['name'],
"vorname" => $row['vorname'],
"programm" => $row['programm'],
"verein" => $row['verein'],
"start" => false,
"musik" => 'nan',
"uniqueid" => $uniqueid];
$jsonData = json_encode($data);
break;
case "start":
if (array_key_exists("start", $oldjson)) {
$oldjson["start"] = true;
$jsonData = json_encode($oldjson);
} else {
echo json_encode(['success' => false, 'message' => 'Turnerin nicht auf Display '.json_encode($oldjson).'; '.$jsonString]);
exit;
}
break;
case "result":
// 1. Get IDs and filter out empty values
$noteLinksId = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['displayIdNoteL']);
$noteRechtsId = db_get_var($mysqli, "SELECT `value` FROM $tableVar WHERE `name` = ?", ['displayIdNoteR']);
// 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 $tableNoten
WHERE person_id = ? AND `jahr` = ? AND `geraet_id` = ?
AND `note_bezeichnung_id` IN ($placeholders)";
$stmt = $mysqli->prepare($sqlNoten);
// Combine standard params with our dynamic ID list
$params = array_merge([$id, $jahr, $geraetId], $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`, `prefix_display`
FROM $tableNotenBezeichnungen 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;
$pre = $conf['prefix_display'] ?? '';
return $pre . number_format((float)$val, (int)$prec, '.', '');
};
// 5. Assign to JSON
$oldjson["noteLinks"] = $formatNote($noteLinksId);
$oldjson["noteRechts"] = $formatNote($noteRechtsId);
$jsonData = json_encode($oldjson);
break;
}
// 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' => 'JSON updated successfully for ' . $geraetName,
'data' => json_decode($jsonData, true),
'nameGeraet' => strtolower($geraetName)
]);
exit;

View File

@@ -0,0 +1,115 @@
<?php
header('Content-Type: application/json');
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
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';
// ---------- Get and sanitize input ----------
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$discipline = isset($_POST['discipline']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_POST['discipline']) : '';
$stmt = $mysqli->prepare("SELECT `name` FROM $tableGeraete ORDER BY start_index ASC");
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
$allowed_disciplines = array_map(
'strtolower',
array_column($result->fetch_all(MYSQLI_ASSOC), 'name')
);
$stmt->close();
if (!in_array($discipline, $allowed_disciplines)) {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
exit;
}
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$result = $mysqli->query("SELECT * FROM `$tableTurnerinnen` WHERE id = $id");
$row = $result->fetch_assoc();
if (!$row) {
echo json_encode(['success' => false, 'message' => 'Row fetch failed']);
exit;
}
$folder = realpath($baseDir . '/displays/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'display_' . $discipline . '.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) ?? [];
if (array_key_exists("note", $oldjson) && array_key_exists("dnote", $oldjson)) {
$oldjson["note"] = (float)$row['note '.$discipline];
$oldjson["dnote"] = (float)$row['d-note '.$discipline];
} else {
echo json_encode([
'success' => false,
'message' => 'ERROR: JSON keys "note" or "dnote" do not exist'
]);
exit;
}
$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 updated successfully for '.$discipline,
]);
exit;

View File

@@ -0,0 +1,97 @@
<?php
header('Content-Type: application/json');
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
// ---------- Get and sanitize input ----------
$discipline = isset($_GET['discipline']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_GET['discipline']) : '';
$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';
$stmt = $mysqli->prepare("SELECT `name` FROM $tableGeraete ORDER BY start_index ASC");
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
$allowed_disciplines = array_map(
'strtolower',
array_column($result->fetch_all(MYSQLI_ASSOC), 'name')
);
$stmt->close();
if (!in_array($discipline, $allowed_disciplines)) {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
exit;
}
$folder = realpath($baseDir . '/displays/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder.'
]);
exit;
}
$filename = 'display_' . $discipline . '.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) ?? [];
if (array_key_exists("start", $oldjson)) {
$oldjson["start"] = true;
$jsonData = json_encode($oldjson);
} else {
echo json_encode(['success' => false, 'message' => 'Turnerin nicht auf Display '.json_encode($oldjson).'; '.$jsonString]);
exit;
}
// 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' => 'JSON updated successfully for '.$discipline,
'disable_start_button' => true
]);
exit;

View File

@@ -0,0 +1,122 @@
<?php
header('Content-Type: application/json');
session_start();
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$type = 'kr';
$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';
// ---------- Get and sanitize input ----------
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$discipline = isset($_GET['discipline']) ? preg_replace('/[^a-zA-Z0-9 _-]/', '', $_GET['discipline']) : '';
$stmt = $mysqli->prepare("SELECT `name` FROM $tableGeraete ORDER BY start_index ASC");
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
$allowed_disciplines = array_map(
'strtolower',
array_column($result->fetch_all(MYSQLI_ASSOC), 'name')
);
$stmt->close();
if (!in_array($discipline, $allowed_disciplines)) {
echo json_encode(['success' => false, 'message' => 'Invalid discipline']);
exit;
}
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$result = $mysqli->query("SELECT name, vorname, verein, programm FROM `$tableTurnerinnen` WHERE id = $id");
$row = $result->fetch_assoc();
if (!$row) {
echo json_encode(['success' => false, 'message' => 'Row fetch failed']);
exit;
}
$folder = realpath($baseDir . '/displays/json');
if ($folder === false) {
echo json_encode([
'success' => false,
'message' => 'Could not find displays folder. Tried: ' . __DIR__ . '/../displays'
]);
exit;
}
$filename = 'display_' . $discipline . '.json';
$filepath = $folder . '/' . $filename;
if (!is_writable($folder)) {
echo json_encode(['success' => false, 'message' => 'Folder not writable: ' . $folder]);
exit;
}
if (file_put_contents($filepath, $jsonData) === false) {
echo json_encode(['success' => false, 'message' => 'Failed to write file: ' . $filepath]);
exit;
}
$jsonString = file_get_contents($folder . $filename);
// decode JSON, fallback to empty array if invalid
$oldjson = json_decode($jsonString, true) ?? [];
// safely get value, default 0 if missing
$olduniqueid = $oldjson['uniqueid'] ?? 0;
$uniqueid = $olduniqueid + 1;
$data = ["note" => 'nan',
"dnote" => 'nan',
"id" => $id,
"name" => $row['name'],
"vorname" => $row['vorname'],
"programm" => $row['programm'],
"verein" => $row['verein'],
"start" => false,
"musik" => 'nan',
"uniqueid" => $uniqueid];
$jsonData = json_encode($data);
// Encode JSON with readable formatting
$jsonData = json_encode($data);
// 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_turnerin_button' => true,
'enable_result_button' => true
]);
exit;

View File

@@ -0,0 +1,160 @@
<?php
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
//ini_set('display_errors', 1);
//ini_set('display_startup_errors', 1);
header('Content-Type: application/json');
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-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
$noten = db_select($mysqli, $tableNotenBezeichnungen, "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;
}
require $baseDir . "/../scripts/string-calculator/string-calculator-functions.php";
$notenRechner = new NotenRechner();
// 1. Build the direct map
// Format: [ Changed_Note_ID => [ "CalcId|GeraetId" => [CalcId, GeraetId] ] ]
$dependencyMap = [];
foreach ($berechnungen as $calc) {
$neededIdsArray = $notenRechner->getBenoetigteIdsComplex($calc['berechnung']);
if (empty($neededIdsArray)) {
continue;
}
$calcId = (int)$calc['id'];
foreach ($neededIdsArray as $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;
if (!isset($dependencyMap[$nId])) {
$dependencyMap[$nId] = [];
}
// Store it as the "little array" you requested: [DependentCalcId, GeraetId]
$dependencyMap[$nId][$nodeKey] = [$calcId, $gId];
}
}
// 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 $tableNotenBezeichnungen SET `berechnung_json` = NULL";
$mysqli->query($resetSql);
// Step 2: Prepare the statement
$updateSql = "UPDATE $tableNotenBezeichnungen 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;

View File

@@ -0,0 +1,201 @@
<?php
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
session_start();
if (empty($_SESSION['access_granted_kampfrichter']) || $_SESSION['access_granted_kampfrichter'] !== true || empty($_SESSION['passcodekampfrichter_id']) || $_SESSION['passcodekampfrichter_id'] < 1) {
http_response_code(403);
exit;
}
header('Content-Type: application/json');
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-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;
if (!isset($_POST['value'])) {
echo json_encode(['success' => false, 'message' => 'Kein Value angegeben']);
exit;
}
$value = 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 $tableNotenBezeichnungen 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, $tableNotenBezeichnungen, "*", "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 && $value > $singleNoteConfig['max_value']) {
echo json_encode(['success' => false, 'message' => "Wert zu hoch (max " . $singleNoteConfig['max_value'].")"]);
exit;
}
if ($singleNoteConfig['min_value'] !== null && $value < $singleNoteConfig['min_value']){
echo json_encode(['success' => false, 'message' => "Wert zu niedrig (min " . $singleNoteConfig['min_value'].")"]);
exit;
}
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("siiii", $value, $person_id, $field_type_id, $gereat_id, $jahr);
$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;
}
// Alle Werte werden von der Datenbank geholt und werden, wenn nicht vorhanden, durch den Standartwert ersetzt.
$alleNoten = db_select($mysqli, $tableNotenBezeichnungen, "id, berechnung, default_value, nullstellen");
$noten = db_select($mysqli, $tableNoten, "`value`, `note_bezeichnung_id`", "`person_id` = ? AND `geraet_id` = ? AND `jahr` = ?", [$person_id, $gereat_id, $jahr]);
$ascArrayDefaultValues = array_column($alleNoten, 'default_value', 'id');
$ascArrayRechnungen = array_column($alleNoten, 'berechnung', 'id');
$existierendeNotenIds = array_column($noten, 'note_bezeichnung_id') ?? [];
$alleNotenIds = array_column($alleNoten, 'id') ?? [];
$nichtExistierendeNotenIds = array_diff($alleNotenIds, $existierendeNotenIds) ?? [];
$noten = array_column($noten, 'value', 'note_bezeichnung_id');
foreach ($nichtExistierendeNotenIds as $neni) {
if (!isset($ascArrayDefaultValues[$neni])) { continue; }
$noten[$neni] = $ascArrayDefaultValues[$neni];
}
// We only want to save the IDs that were actually recalculated
$idsToSave = [];
foreach ($abhaenigeRechnungen as $sRechnung) {
$rechnung = $ascArrayRechnungen[$sRechnung] ?? null;
if ($rechnung === null) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, Fehler: Rechnung $sRechnung nicht gefunden"]);
exit;
}
$calcResult = $notenRechner->berechneString($rechnung, $noten);
if ($calcResult['success'] !== true) {
echo json_encode(['success' => true, 'message' => "Wert aktualisiert, Rechenfehler: " . ($calcResult['value'] ?? '')]);
exit;
}
// Update the local array so the NEXT calculation in the loop can use this new value
$noten[$sRechnung] = $calcResult['value'];
// Track that this ID needs to be written to the database
$idsToSave[] = $sRechnung;
}
// Prepare the statement once
$sql = "INSERT INTO $tableNoten (`value`, `person_id`, `note_bezeichnung_id`, `geraet_id`, `jahr`)
VALUES (?, ?, ?, ?, ?)
ON DUPLICATE KEY UPDATE `value` = VALUES(`value`)";
$stmt = $mysqli->prepare($sql);
foreach ($idsToSave as $notenId) {
$currentValue = $noten[$notenId]; // Get the calculated value from our array
// i = integer, s = string (use 's' for decimals/floats to prevent rounding issues in PHP)
$stmt->bind_param("siiii", $currentValue, $person_id, $notenId, $gereat_id, $jahr);
$stmt->execute();
}
$stmt->close();
$mysqli->close();
// 1. Combine the ID the user sent with the IDs we recalculated
$changedIds = array_merge([$field_type_id], $idsToSave);
// 2. Filter the $noten array to only include these keys
// array_flip turns [10, 20] into [10 => 0, 20 => 1] so we can intersect by keys
$changedNoten = array_intersect_key($noten, array_flip($changedIds));
$indexedNullstellen = array_column($alleNoten, 'nullstellen', 'id');
foreach ($changedNoten as $key => $scN) {
$changedNoten[$key] = number_format($scN ,$indexedNullstellen[$key] ?? 2);
}
echo json_encode([
'success' => true,
'message' => "Wert aktualisiert, alle Berechnungen durchgeführt",
"noten" => $changedNoten
]);

View File

@@ -0,0 +1,192 @@
jQuery(document).ready(function($) {
$('.ranglisteExport').on('click', function() {
const $button = $(this);
const progId = $button.data('id');
const fieldType = $button.data('field_type');
// Visual feedback (optional but recommended)
$button.prop('disabled', true).text('Generating...');
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({
prog: progId,
type: fieldType
})
})
.then(response => {
// CRITICAL: Check if the server actually returned a success code
if (!response.ok) {
throw new Error('Server returned an error (Status ' + response.status + ')');
}
return response.blob();
})
.then(blob => {
if (fieldType !== 'upload_programm') {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `Ergebnisse_${progId}.pdf`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
window.URL.revokeObjectURL(url);
} else {
alert('PDF auf Webseite geladen!');
}
})
.catch(error => {
console.error('Export Error:', error);
alert('Fehler beim Exportieren der Rangliste.');
})
.finally(() => {
// Restore button state
$button.prop('disabled', false).text('Export PDF');
});
});
$('.protokollExport').on('click', function() {
console.log('ok');
const $input = $(this);
// Build the data to send
const data = new URLSearchParams();
data.append('abteilung', $input.data('abteilung'));
// Record start time
const start = performance.now();
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({
abteilung: $input.data('abteilung')
})
})
.then(res => res.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = "KTBB_Protokoll.pdf"; // optional
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
});
});
let activeEdit = null;
console.log('pre');
$(document).on('click', '.editableValue', function () {
console.log('ok');
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;
}
});

View File

@@ -0,0 +1,876 @@
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();
}
});
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 = [];
function displayMsg(type, msg) {
const colors = ["#900000ff", "#00b200ff"];
if (type !== 0 && type !== 1) return;
// idx is ALWAYS a valid non-negative index now
const $div = $('<div class="msgBox"></div>')
.css({'border-color': colors[type]})
.text(msg);
$('.msgDiv').append($div);
// trigger entry animation
setTimeout(() => {
$div.addClass('show');
}, 200);
setTimeout(() => {
// First: set the transition properties
$div.css({
'transition': 'all 0.3s ease'
});
// Next frame: apply the transform so the transition animates
requestAnimationFrame(() => {
$div.removeClass('show');
$div.css({
'transform': 'scaleY(0) translateX(calc(100% + 40px))'
});
});
}, 3000);
// auto-remove
setTimeout(() => {
$div.remove();
}, 3500);
}
const text = document.getElementById('wsInfo');
const rect = document.getElementById('wsInfoRectangle');
let ws;
let firstConnect = true;
const RETRY_DELAY = 2000; // ms
const urlAjaxNewWSToken = '/intern/scripts/ajax-create-ws-token.php';
async function fetchNewWSToken(freigabe) {
try {
const response = await fetch(urlAjaxNewWSToken, {
method: "POST",
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
},
body: new URLSearchParams({ access: freigabe })
});
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('kampfrichter');
}
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;
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) => {
displayMsg(0, "Live Syncronisation verloren");
firstConnect = 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();
$.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: '>'
}))
.appendTo('body')
.submit();
}
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
csrf_token: window.CSDR_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");
$(".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 programmId = response.programm_id;
// First, reset all containers to a single input state and clear values
$editAllDiv.find('.note-container').each(function() {
const $container = $(this);
const $tbody = $container.find('tbody');
const $headerRow = $container.find('thead tr');
// Reset header
$headerRow.find('.run-num-header').remove();
// Remove extra inputs beyond run 1
$tbody.find('.inputs-row').each(function() {
$(this).find('td:not(.input-cell-run-1)').remove();
});
// Clear value of run 1
$container.find('input[data-run="1"]').val('').attr('data-person-id', response.id);
});
// Now loop through the data and populate/expand
for (const [geraetId, noteGroup] of Object.entries(noten)) {
for (const [noteId, runGroup] of Object.entries(noteGroup)) {
const $container = $editAllDiv.find(`.note-container[data-note-id="${noteId}"]`);
if ($container.length === 0) continue;
const $tbody = $container.find('tbody');
const $headerRow = $container.find('thead tr');
const $inputsRow = $tbody.find('.inputs-row');
const runCount = Object.keys(runGroup).length;
$headerRow.find('.note-name-header .rm').remove();
const originalText = $headerRow.find('.note-name-header').text().trim();
// If more than 1 run, add headers if not already present
if (runCount > 1 && $headerRow.find('.run-num-header').length === 0) {
$headerRow.find('.note-name-header').html(originalText + ' <span class="rm">(R1)</span>');
for (let r = 2; r <= runCount; r++) {
$headerRow.append(`<th class="run-num-header">${originalText} <span class="rm">(R${r})</span></th>`);
}
}
for (const [runNum, val] of Object.entries(runGroup)) {
let $input = $inputsRow.find(`input[data-run="${runNum}"][data-geraet-id="${geraetId}"]`);
// If input doesn't exist yet (for Run 2+), clone it
if ($input.length === 0) {
const $cell1 = $inputsRow.find('.input-cell-run-1');
const $newCell = $cell1.clone();
$newCell.removeClass('input-cell-run-1').addClass(`input-cell-run-${runNum}`);
$input = $newCell.find('input');
$input.attr('data-run', runNum).val(val ?? '');
$inputsRow.append($newCell);
// Re-bind change event to new input
//bindAjaxInput($input);
} else {
$input.val(val ?? '').attr('data-person-id', response.id);
}
}
}
}
$(".submit-display-turnerin").attr('data-person-id', response.id);
$(".submit-display-start").attr('data-person-id', response.id);
const submitMusikStart = $(".submit-musik-start");
const submitMusikStopp = $(".submit-musik-stopp");
if (submitMusikStart.length > 0 && submitMusikStopp.length > 0){
submitMusikStart.attr('data-id', response.id);
submitMusikStopp.attr('data-id', response.id);
}
$(".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);
});
});
};
jQuery(document).ready(function($) {
$('.editTurnerin').on('click', function() {
$(this).updateCurrentEdit();
});
function bindAjaxInput($el) {
$el.on('change', function() {
const start = performance.now();
const $input = $(this);
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_value_kampfrichter.php`;
personId = $input.data('person-id');
fieldTypeId = $input.data('field-type-id');
gereatId = $input.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({
personId: personId,
fieldTypeId: fieldTypeId,
gereatId: gereatId,
run: runNum,
jahr: jahr,
value: value
})
})
.then(res => res.json())
.then(response => {
const end = performance.now();
console.log(`Total AJAX time: ${(end - start).toFixed(3)} ms`);
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 [keyN, noteGroup] of Object.entries(noten)) {
for (const [key, runGroup] of Object.entries(noteGroup)) {
if (key == fieldTypeId && keyN == gereatId) { continue; }
for (const [run, value] of Object.entries(runGroup)) {
$(`input.changebleValue[data-field-type-id="${key}"][data-geraet-id="${keyN}"][data-person-id="${personId}"][data-run="${run}"]`)
.val(value ?? '');
$(`.changebleValue:not(input)[data-field-type-id="${key}"][data-geraet-id="${keyN}"][data-person-id="${personId}"][data-run="${run}"]`)
.text(value ?? '');
}
}
}
ws.send(JSON.stringify({
type: "KAMPFRICHTER_UPDATE",
payload: {
discipline: window.FREIGABE,
gereatId: gereatId,
personId: personId,
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);
});
});
}
$('.ajax-input').each(function() {
bindAjaxInput($(this));
});
/*$('.ranglisteExport').on('click', function(e) {
const $input = $(this);
if ($input.data('field_type') !== 'upload_programm') {e.preventDefault();}
// Build the data to send
const data = new URLSearchParams();
data.append('prog', $input.data('id'));
data.append('type', $input.data('field_type'));
// Record start time
const start = performance.now();
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({
prog: $input.data('id'),
type: $input.data('field_type')
})
})
.then(res => res.blob())
.then(blob => {
if ($input.data('field_type') !== 'upload_programm'){
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = "KTBB_Ergebnisse.pdf"; // optional
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} else {
alert('PDF auf Webseite geladen!');
}
});
});
$('.protokollExport').on('click', function() {
console.log('ok');
const $input = $(this);
// Build the data to send
const data = new URLSearchParams();
data.append('abteilung', $input.data('abteilung'));
// Record start time
const start = performance.now();
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({
abteilung: $input.data('abteilung')
})
})
.then(res => res.blob())
.then(blob => {
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = "KTBB_Protokoll.pdf"; // optional
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
});
});*/
$('.inputnamekr').on('change', function() {
const $input = $(this);
const url = '/intern/scripts/kampfrichter/ajax/ajax-update_name_kampfrichter_protokoll.php';
fetch(url, {
method: 'POST',
headers: {'Content-Type': 'application/x-www-form-urlencoded'},
body: new URLSearchParams({
aufgabe: $input.data('id'),
abteilung: $input.data('abt'),
geraet: $input.data('user'),
name: $input.val()
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
console.log(response.message);
$input.css({
'background-color': '#a4bf4a',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
} else {
console.error(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);
displayMsg(0, 'AJAX fetch error:' + err);
});
});
/**
* Handle namekr input updates
$('.ajax-input-namekr').on('change', function() {
let $input = $(this);
$.post(ajax_object.ajaxurl, {
action: 'save_namekr_input',
id: $input.data('id'),
abt: $input.data('abt'),
user: $input.data('user'),
value: $input.val()
}, function(response) {
if (response.success) {
console.log(response.data.message);
$input.css({
'background-color': '#a4bf4a',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
} else {
console.error(response.data.message);
$input.css({
'background-color': '#f8d7da',
'color': '#fff',
'transition': 'all 0.3s ease-out'
});
setTimeout(() => $input.css({
'background-color': '',
'color': ''
}), 2000);
}
}, 'json');
}); */
/**
* Handle display JSON updates
*/
$('.submit-display-turnerin').on('click', function() {
const $input = $(this);
// 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({
personId: $input.attr('data-person-id'),
geraetId: $input.attr('data-geraet-id'),
jahr: window.AKTUELLES_JAHR,
type: "neu"
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
displayMsg(1, 'Neue Turnerin 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 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({
geraetId: $input.attr('data-geraet-id'),
type: "start"
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
displayMsg(1, 'Start freigegeben');
$input.css('opacity', 0.5);
} else {
alert('Error: ' + 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() {
let discipline = $(this).data('discipline');
$.post(ajax_object.ajaxurl, {
action: 'write_discipline_json_start',
discipline: discipline
}, function(response) {
if (response.success) {
alert('Start freigegeben');
if (response.data.disable_start_button) {
$('.submit-display-start').css({
'border': '1px solid #aaa',
'background-color': '#aaa',
'color': '#555',
'pointer-events': 'none'
});
$('.submit-musik-start').css({
'background-color': '#077',
'border': '1px solid #077',
'color': '#fff',
'cursor': 'pointer',
'pointer-events': 'auto'
});
}
} else {
alert('Error: ' + response.data.message);
}
}, 'json');
});*/
$('.submit-musik-start').on('click', function() {
const $input = $(this);
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_start_musik.php` +
`?id=${$input.attr('data-id')}` +
`&discipline=${encodeURIComponent($input.data('geraet'))}`;
fetch(url)
.then(res => res.json())
.then(response => {
const end = performance.now();
ws.send(JSON.stringify({
type: "AUDIO",
payload: {}
}));
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);
// Build the URL with GET parameters safely
const url = `/intern/scripts/kampfrichter/ajax/ajax-update_kampfrichter_stopp_musik.php`;
fetch(url)
.then(res => res.json())
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "AUDIO",
payload: {}
}));
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);
});
});
$('.submit-display-result').on('click', function() {
$input = $(this);
// 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({
personId: $input.attr('data-person-id'),
geraetId: $input.attr('data-geraet-id'),
jahr: window.AKTUELLES_JAHR,
type: "result"
})
})
.then(res => res.json())
.then(response => {
if (response.success) {
ws.send(JSON.stringify({
type: "UPDATE_SCORE",
payload: {
geraet: response.nameGeraet,
data: response.data
}
}));
$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);
});
});
});
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.toLowerCase() === 'admin') {
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)) {
// OPTIONAL: Skip if the current user is currently focused on this specific input
if (document.activeElement.dataset.fieldTypeId === key &&
document.activeElement.dataset.geraetId === keyG &&
document.activeElement.dataset.run == run) continue;
// 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(function() {
const $el = $(this);
// Update value or text
if ($el.is('input')) {
$el.val(value ?? '');
} else {
$el.text(value ?? '');
}
// Visual feedback: Flash color
const nativeEl = $el[0];
nativeEl.style.transition = 'color 0.3s';
nativeEl.style.color = '#008e85';
setTimeout(() => {
nativeEl.style.color = '';
}, 1000);
});
}
}
}
}
}
});
/*document.getElementById('freigabe-select').addEventListener('change', function() {
const freigabe = this.value;
const user_id = document.getElementById('user_id').value;
const nonce = document.getElementById('freigabe_nonce').value;
const type = document.getElementById('type_freigabe').value;
const params = new URLSearchParams();
params.append('action', 'save_freigabe');
params.append('freigabe', freigabe);
params.append('user_id', user_id);
params.append('type', type);
fetch('/intern/scripts/kampfrichter/ajax/ajax-update_selected_kampfrichter.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();
} else {
alert('Error: ' + data.data);
}
})
.catch(err => {
console.error(err);
displayMsg(0, 'AJAX fetch error:' + err);
});
});*/

View File

@@ -0,0 +1,34 @@
function allNameKrValid() {
var isValid = true;
$('.ajax-input-namekr').each(function () {
if ($(this).val() === '---') {
isValid = false;
return false; // break loop
}
});
return isValid;
}
function updateSelectKrDiv() {
const krDiv = $('.noKampfrichterDiv');
if (krDiv.length !== 1) {
return;
}
if (allNameKrValid()) {
krDiv.css('display', 'none');
} else {
krDiv.css('display', 'flex');
}
}
// initial check
updateSelectKrDiv();
// react to changes
$('.ajax-input-namekr').on('change', function () {
updateSelectKrDiv();
});

View File

@@ -0,0 +1,84 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
$token = isset($_GET['token']) ? $_GET['token'] : '';
if ($token !== 'sWZ4GxbsoVhUPk5zhjH0uU9hets3zV2KsV8CZUvAWCCRk4uuuDr9vfFVgxWqr5FtDttbtm50EdWK9YxuMPswGZBQZFHAUAET1aG1'){
echo json_encode(['success' => false, 'message' => '500 Error - Critical Server Error']);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
$value = isset($_GET['value']) ? preg_replace('/[^a-zA-Z0-9\s\-"]/u', '', $_GET['value']) : '';
if (!$value || $value === ''){
echo json_encode(['success' => false, 'message' => 'No input']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$stmt = $mysqli->prepare("INSERT INTO `$tableProgramme` (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 `$tableProgramme` 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'])
];
}
// Return JSON
echo json_encode([
'success' => true,
'output' => $output,
'message' => $value.' hinzugefügt'
]);
exit;

View File

@@ -0,0 +1,89 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
$token = isset($_GET['token']) ? $_GET['token'] : '';
if ($token !== 'bKqBAPjwojZdarJaE7jwvRrIEf2WzJUlFlufQadfLJ98qJcrWZK5pRlGoUQOHp1L06urGRbEdE9v5oIRirPiUCjm93wATghO4qx'){
echo json_encode(['success' => false, 'message' => '500 Error - Critical Server Error']);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$stmt = $mysqli->prepare("DELETE FROM `$tableProgramme` WHERE id = ?");
if (!$stmt) {
echo json_encode(['success' => false, 'message' => 'Critical DB ERROR']);
exit;
}
$stmt->bind_param("i", $id);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
echo json_encode(['success' => false, 'message' => 'Insert failed']);
exit;
}
// Fetch all rows
$query2 = "SELECT * FROM `$tableProgramme` 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'])
];
}
// Return JSON
echo json_encode([
'success' => true,
'output' => $output,
'message' => $id.' gelöscht'
]);
exit;

View File

@@ -0,0 +1,159 @@
<?php
use Dotenv\Dotenv;
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_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;
}
if (isset($_POST['field_id'])){
$id = intval($_POST['field_id']);
} else {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
exit;
}
$editor_id = $_SESSION['passcodewk_leitung_id'];
$plain = trim($_POST['password'] ?? '');
if (!$plain) {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
exit;
}
$username = htmlspecialchars(trim($_POST['username'] ?? ''));
if (!$username) {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
exit;
}
$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 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);
if ($id > 0) {
$updated = db_update($mysqli, $tableInternUsers, [
'password_hash' => $hash,
'password_cipher' => $cipher_store,
'username' => $username,
'freigabe' => $freigabe_store,
'updated_at' => date('Y-m-d H:i:s'),
'edited_by' => $editor_id
], ['id' => $id]);
} else {
$stmt = $mysqli->prepare(
"INSERT INTO {$tableInternUsers}
(username, password_hash, password_cipher, freigabe, created_at, updated_at, edited_by)
VALUES (?, ?, ?, ?, ?, ?, ?)"
);
$stmt->bind_param(
"ssssssi",
$username,
$hash,
$cipher_store,
$freigabe_store,
$created_at,
$updated_at,
$editor_id
);
$created_at = date('Y-m-d H:i:s');
$updated_at = $created_at;
$updated = $stmt->execute();
}
if ($updated !== false) {
if ($id == 0) { // new user
$new_id = $mysqli->insert_id;
echo json_encode(['success' => true, 'message' => $username.' wurde erfolgreich erstellt.', 'id' => $new_id]);
} else {
echo json_encode(['success' => true, 'message' => $username.' wurde erfolgreich aktualisiert.']);
}
} else {
echo json_encode(['success' => false, 'message' => 'DB Error']);
}

View File

@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
$token = isset($_GET['token']) ? $_GET['token'] : '';
if ($token !== '0UgBVHutbxTRTYsB04ujFKMjMRA8GgdqRJjVh3DKU1LRJfwtcDfrpDc7jpMxcrg9rYurAEwYPy5gu15R77MsgKsDMkFZEykx0A67'){
echo json_encode(['success' => false, 'message' => '500 Error - Critical Server Error']);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
// ---------- Get and sanitize input ----------
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$value = isset($_GET['value']) ? floatval($_GET['value']) : 0;
if ($id < 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$query = "UPDATE `$tableProgramme` SET aktiv = '$value' WHERE id = $id";
$result = $mysqli->query($query);
if (!$result) {
echo json_encode(['success' => false, 'message' => 'Update failed']);
exit;
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'Aktualisiert'
]);
exit;

View File

@@ -0,0 +1,60 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
$token = isset($_GET['token']) ? $_GET['token'] : '';
if ($token !== 'k7uweziEUWZiJhwe7687UWIQZ28SQIH2ug74pINKyxHxPerB6wUZ'){
echo json_encode(['success' => false, 'message' => '500 Error - Critical Server Error']);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
// ---------- Get and sanitize input ----------
$id = isset($_GET['id']) ? intval($_GET['id']) : 0;
$value = isset($_GET['value']) ? round(floatval($_GET['value']), 2) : 0;
if ($id < 0) {
echo json_encode(['success' => false, 'message' => 'Invalid ID']);
exit;
}
// ---------- Step 2: Get values from DB ----------
$query = "UPDATE `$tableProgramme` SET preis = '$value' WHERE id = $id";
$result = $mysqli->query($query);
if (!$result) {
echo json_encode(['success' => false, 'message' => 'Update failed']);
exit;
}
// ---------- Return JSON ----------
echo json_encode([
'success' => true,
'message' => 'Startgebüren set to '.$value.' Fr.'
]);
exit;

View File

@@ -0,0 +1,67 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_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;
}
$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, $tableVereine, [
'verein' => $verein,
], ['id' => $id]);
} else {
$stmt = $mysqli->prepare(
"INSERT INTO {$tableVereine}
(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']);
}

View File

@@ -0,0 +1,46 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_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;
}
$id = intval($_POST['field_id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'Invalid Input.']);
exit;
}
$deleted = db_delete($mysqli, $tableInternUsers, ['id' => $id]);
if ($deleted !== true) {
// DB query failed (syntax/connection issue)
echo json_encode(['success' => false, 'message' => 'DB Error.']);
exit;
} else {
// Success
echo json_encode(['success' => true, 'message' => "Benutzer $id erfolgreich gelöscht.", 'id' => $id]);
exit;
}

View File

@@ -0,0 +1,36 @@
<?php
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
}
$id = intval($_POST['field_id'] ?? 0);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'No valid ID']);
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_functions.php';
$type = 'wkl';
$dbconnection = require $baseDir . '/../scripts/db/db-verbindung-script.php';
if ($dbconnection['success'] !== true){
echo 'Critical DB Error.';
exit;
}
if (!db_delete($mysqli, $tableVereine, ['id' => $id])) {
echo json_encode(['success' => false, 'message' => 'Failed to delete verein']);
exit;
}
echo json_encode(['success' => true, 'message' => '200, ok']);

View File

@@ -0,0 +1,61 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
require $baseDir . '/../scripts/db/db-functions.php';
require $baseDir . '/../scripts/db/db-tables.php';
require $baseDir . '/../scripts/csrf_functions.php';
$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);
if ($id <= 0) {
echo json_encode(['success' => false, 'message' => 'No valid ID']);
exit;
}
// Delete old OTL links for this user (recommended)
db_delete($mysqli, $tableOTL, ['user_id' => $id]);
// Insert the row — url + timestamp are auto-generated by MySQL
$stmt = $mysqli->prepare("INSERT INTO {$tableOTL} (user_id) VALUES (?)");
$stmt->bind_param("i", $id);
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 $tableOTL 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]);

View File

@@ -0,0 +1,73 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
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), '?'));
// Prepare the SQL statement
$sql = "DELETE FROM $tableOrders 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;
}
// Bind parameters dynamically
$types = str_repeat('i', count($ids)); // all integers
$stmt->bind_param($types, ...$ids);
// Execute
if (!$stmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler beim Löschen']);
http_response_code(500);
exit;
}
$stmt->close();
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Bestellungen erfolgreich gelöscht']);
http_response_code(200);
exit;

View File

@@ -0,0 +1,69 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
if (!isset($_POST['scor']) || empty($_POST['scor']) || $_POST['scor'] === '') {
echo json_encode(['success' => false, 'message' => 'Keine Referenz angegeben']);
http_response_code(422);
exit;
}
$scorFullStr = strval($_POST['scor']);
$scor = intval(substr($scorFullStr, 2));
$stmt = $mysqli->prepare(
"SELECT 1 FROM $tableOrders 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();
$sql = "UPDATE $tableOrders SET order_status = 2 WHERE order_id = ?";
$nstmt = $mysqli->prepare($sql);
$nstmt->bind_param('i', $scor);
if (!$nstmt->execute()) {
echo json_encode(['success' => false, 'message' => 'Fehler']);
http_response_code(500);
exit;
}
$nstmt->close();
$mysqli->close();
echo json_encode(['success' => true, 'message' => 'Rechnung wurde aktualisiert']);
http_response_code(200);
exit;

View File

@@ -0,0 +1,233 @@
<?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 (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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, $tableGeraete, "id, name, start_index", "", [], "start_index ASC");
$allAbt = db_select($mysqli, $tableAbt, "name, id", "", [], "name ASC");
$maxTurnerinnenGruppe = 8;
// $allGeraete[] = ['name' => 'null', 'id' => 'null'];
// $allAbt[] = ['name' => 'null', 'id' => 'null'];
/*$stmt = $mysqli->prepare("
SELECT
t.id,
t.programm,
t.verein,
GROUP_CONCAT(ta.id SEPARATOR ', ') AS abt_table_id
FROM $tableTurnerinnen t
LEFT JOIN $tableTurnerinnenAbt ta ON ta.turnerin_id = t.id
GROUP BY t.id, t.programm, t.verein
ORDER BY t.id DESC
");
$stmt->execute();
$result = $stmt->get_result();
$allTurnerinnen = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
print_r($allTurnerinnen);*/
$allTurnerinnen = db_select($mysqli, $tableTurnerinnen, "id, programm, verein, name" , "", [], "programm ASC");
$grouped = [];
foreach ($allTurnerinnen as $t) {
$sanitasedprogramm = str_replace(['-kader'], '', strtolower($t['programm']));
$grouped[$sanitasedprogramm][$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);
$stmt = $mysqli->prepare("DELETE FROM $tableTurnerinnenAbt");
$stmt->execute();
$stmt->close();
$stmt = $mysqli->prepare("DELETE FROM $tableAbt");
$stmt->execute();
$stmt->close();
foreach ($arrayAutoRiegeneinteilung as $indAbt => $abt) {
$stmt = $mysqli->prepare("INSERT INTO $tableAbt (name) VALUES (?)");
$stmt->bind_param("s", $indAbt);
$stmt->execute();
$idAbt = $stmt->insert_id;
$stmt->close();
foreach ($abt as $indGeraet => $geraet) {
foreach ($geraet as $indTurnerin => $turnerin) {
$stmt = $mysqli->prepare("INSERT INTO $tableTurnerinnenAbt (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(201);
/*foreach ($grouped as $ind => $g) {
echo $ind;
foreach ($g as $verein) {
foreach ($verein as $turnerin) {
echo '<div class="riegeneinteilung_turnerin_row" data-turnerin_id="' . $turnerin['id'] . '">';
echo '<div class="riegeneinteilung_turnerin_info">';
echo '<span class="riegeneinteilung_turnerin_name">' . htmlspecialchars($turnerin['name']) . '</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['verein']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['abt']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['geraet_id']) . ')</span>';
echo '</div>';
echo '</div></div>';
}
}
echo '<br><br>';
}
foreach ($arrayAutoRiegeneinteilung as $ind => $abt) {
echo '<br>ABT:' . $ind;
foreach ($abt as $g) {
echo '<br>GERÄT';
foreach ($g as $turnerin) {
echo '<div class="riegeneinteilung_turnerin_row" data-turnerin_id="' . $turnerin['id'] . '">';
echo '<div class="riegeneinteilung_turnerin_info">';
echo '<span class="riegeneinteilung_turnerin_name">' . htmlspecialchars($turnerin['name']) . '</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['verein']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['turnerin_index']) . ')</span>';
echo '<span class="riegeneinteilung_turnerin_verein">(' . htmlspecialchars($turnerin['programm']) . ')</span>';
echo '</div>';
echo '</div></div>';
}
}
echo '<br><br>';
}
print_r($arrayAutoRiegeneinteilung);*/

View File

@@ -0,0 +1,66 @@
<?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 (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
// Validate input
if (!isset($_POST['anz_abt']) || !ctype_digit($_POST['anz_abt'])) {
http_response_code(406);
exit;
}
$anz_abt = (int) $_POST['anz_abt'];
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
// Fetch existing Abteilungen
$abts = db_select($mysqli, $tableAbt, '*');
// Delete excess Abteilungen
foreach ($abts as $dbabt) {
if ((int)$dbabt['name'] > $anz_abt) {
db_delete($mysqli, $tableAbt, ['name' => $dbabt['name']]);
}
}
// Build lookup of existing names
$existing = array_map('intval', array_column($abts, 'name'));
// Insert missing Abteilungen
$stmt = $mysqli->prepare("INSERT INTO $tableAbt (name) VALUES (?)");
for ($i = 1; $i <= $anz_abt; $i++) {
if (!in_array($i, $existing, true)) {
$stmt->bind_param('i', $i);
$stmt->execute();
}
}
$stmt->close();
http_response_code(201);

View File

@@ -0,0 +1,74 @@
<?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 (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
// Validate input
if (!isset($_POST['abt']) || !ctype_digit($_POST['abt'])) {
http_response_code(406);
exit;
}
$abtInput = (int) $_POST['abt'];
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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';
// Fetch existing Abteilungen ordered by name
$abts = db_select($mysqli, $tableAbt, '*', '', [], 'name ASC');
$deleteId = null;
$deleteIndex = null;
foreach ($abts as $ind => $abt) {
if ((int)$abt['name'] === (int)$abtInput) {
$deleteId = (int)$abt['id'];
$deleteIndex = $ind;
break;
}
}
if ($deleteId === null) {
http_response_code(406);
exit;
}
// Delete selected row
db_delete($mysqli, $tableAbt, ['id' => $deleteId]);
// Reindex subsequent rows
for ($i = $deleteIndex + 1; $i < count($abts); $i++) {
db_update(
$mysqli,
$tableAbt,
['name' => (int)$abts[$i]['name'] - 1],
['id' => (int)$abts[$i]['id']]
);
}
http_response_code(201);

View File

@@ -0,0 +1,57 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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-tables.php';
$input = json_decode(file_get_contents('php://input'), true);
if (!$input || !isset($input['new'])) {
http_response_code(400);
exit;
}
$mysqli->begin_transaction();
try {
$stmt = $mysqli->prepare("
UPDATE $tableTurnerinnenAbt
SET turnerin_index = ?
WHERE turnerin_id = ?
");
foreach (['new', 'old'] as $bucket) {
if (empty($input[$bucket])) continue;
foreach ($input[$bucket] as $row) {
$stmt->bind_param("ii", $row['order_index'], $row['id']);
$stmt->execute();
}
}
$mysqli->commit();
http_response_code(200);
} catch (Throwable $e) {
$mysqli->rollback();
http_response_code(500);
}

View File

@@ -0,0 +1,74 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_wk_leitung']) || $_SESSION['access_granted_wk_leitung'] !== true || empty($_SESSION['passcodewk_leitung_id']) || intval($_SESSION['passcodewk_leitung_id']) < 0 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) {
$baseDir = $_SERVER['DOCUMENT_ROOT'];
}
$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-tables.php';
$turnerinId = (int)$_POST['turnerin_id'];
$abteilung = $_POST['abteilung'];
$geraet = $_POST['geraet'];
// Default to NULL if frontend sends "null"
$abtId = null;
$geraetId = null;
// Resolve Abteilung ID
if ($abteilung !== 'null') {
$stmt = $mysqli->prepare("SELECT id FROM $tableAbt WHERE name = ?");
$stmt->bind_param("s", $abteilung);
$stmt->execute();
if ($row = $stmt->get_result()->fetch_assoc()) {
$abtId = (int)$row['id'];
}
$stmt->close();
}
// Resolve Gerät ID
if ($geraet !== 'null') {
$stmt = $mysqli->prepare("SELECT id FROM $tableGeraete WHERE name = ?");
$stmt->bind_param("s", $geraet);
$stmt->execute();
if ($row = $stmt->get_result()->fetch_assoc()) {
$geraetId = (int)$row['id'];
}
$stmt->close();
}
// Upsert into turnerinnen_abt (turnerin_id is UNIQUE)
$stmt = $mysqli->prepare("
INSERT INTO $tableTurnerinnenAbt (turnerin_id, abteilung_id, geraet_id)
VALUES (?, ?, ?)
ON DUPLICATE KEY UPDATE
abteilung_id = VALUES(abteilung_id),
geraet_id = VALUES(geraet_id)
");
$stmt->bind_param("iii", $turnerinId, $abtId, $geraetId);
$stmt->execute();
$stmt->close();
// Return JSON
http_response_code(200);
echo json_encode([
'success' => true,
'turnerin_id' => $turnerinId,
'abteilung_id' => $abtId,
'geraet_id' => $geraetId
]);

View File

@@ -0,0 +1,204 @@
<?php
/**
* Shared Sidebar Navigation
*
* Include this file after <body> on any intern page.
* Set $currentPage before including, e.g.:
* $currentPage = 'trainer';
* require $baseDir . '/intern/scripts/sidebar/sidebar.php';
*/
$isWKL = $_SESSION['access_granted_wk_leitung'] ?? false;
$isTrainer = $_SESSION['access_granted_trainer'] ?? false;
$isKampfrichter = $_SESSION['access_granted_kampfrichter'] ?? false;
if (!isset($currentPage)) $currentPage = '';
if (isset($_POST['abmelden'])) {
if (session_status() === PHP_SESSION_ACTIVE) {
$_SESSION = array();
session_destroy();
}
header("Location: " . $_SERVER['PHP_SELF']);
exit;
}
// SVG Icons (stroke-based, 24x24 viewBox)
$icons = [
'trainer' => '<svg viewBox="0 0 24 24"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"/><circle cx="9" cy="7" r="4"/><path d="M23 21v-2a4 4 0 0 0-3-3.87"/><path d="M16 3.13a4 4 0 0 1 0 7.75"/></svg>',
'kampfrichter' => '<svg viewBox="0 0 24 24"><path d="M12 22s8-4 8-10V5l-8-3-8 3v7c0 6 8 10 8 10z"/></svg>',
'rechnungen' => '<svg viewBox="0 0 24 24"><path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z"/><polyline points="14 2 14 8 20 8"/><line x1="16" y1="13" x2="8" y2="13"/><line x1="16" y1="17" x2="8" y2="17"/><polyline points="10 9 9 9 8 9"/></svg>',
'logindata' => '<svg viewBox="0 0 24 24"><rect x="3" y="11" width="18" height="11" rx="2" ry="2"/><path d="M7 11V7a5 5 0 0 1 10 0v4"/></svg>',
'displaycontrol' => '<svg viewBox="0 0 24 24"><rect x="2" y="3" width="20" height="14" rx="2" ry="2"/><line x1="8" y1="21" x2="16" y2="21"/><line x1="12" y1="17" x2="12" y2="21"/></svg>',
'kalender' => '<svg viewBox="0 0 24 24"><rect x="3" y="4" width="18" height="18" rx="2" ry="2"/><line x1="16" y1="2" x2="16" y2="6"/><line x1="8" y1="2" x2="8" y2="6"/><line x1="3" y1="10" x2="21" y2="10"/></svg>',
'riegeneinteilung' => '<svg viewBox="0 0 24 24"><path d="M16 4h2a2 2 0 0 1 2 2v14a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h2"/><rect x="8" y="2" width="8" height="4" rx="1" ry="1"/><line x1="9" y1="12" x2="15" y2="12"/><line x1="9" y1="16" x2="15" y2="16"/></svg>',
'einstellungen' => '<svg viewBox="0 0 24 24"><circle cx="12" cy="12" r="3"></circle><path d="M19.4 15a1.65 1.65 0 0 0 .33 1.82l.06.06a2 2 0 0 1 0 2.83 2 2 0 0 1-2.83 0l-.06-.06a1.65 1.65 0 0 0-1.82-.33 1.65 1.65 0 0 0-1 1.51V21a2 2 0 0 1-2 2 2 2 0 0 1-2-2v-.09A1.65 1.65 0 0 0 9 19.4a1.65 1.65 0 0 0-1.82.33l-.06.06a2 2 0 0 1-2.83 0 2 2 0 0 1 0-2.83l.06-.06a1.65 1.65 0 0 0 .33-1.82 1.65 1.65 0 0 0-1.51-1H3a2 2 0 0 1-2-2 2 2 0 0 1 2-2h.09A1.65 1.65 0 0 0 4.6 9a1.65 1.65 0 0 0-.33-1.82l-.06-.06a2 2 0 0 1 0-2.83 2 2 0 0 1 2.83 0l.06.06a1.65 1.65 0 0 0 1.82.33H9a1.65 1.65 0 0 0 1-1.51V3a2 2 0 0 1 2-2 2 2 0 0 1 2 2v.09a1.65 1.65 0 0 0 1 1.51 1.65 1.65 0 0 0 1.82-.33l.06-.06a2 2 0 0 1 2.83 0 2 2 0 0 1 0 2.83l-.06.06a1.65 1.65 0 0 0-.33 1.82V9a1.65 1.65 0 0 0 1.51 1H21a2 2 0 0 1 2 2 2 2 0 0 1-2 2h-.09a1.65 1.65 0 0 0-1.51 1z"></path></svg>',
];
if (isset($mysqli) && isset($tableInternUsers)) {
if ($currentPage === 'kampfrichter' && checkIfUserHasSessionId('kampfrichter')):
$userDispId = intval($_SESSION['passcodekampfrichter_id']);
elseif ($currentPage === 'kampfrichter' && checkIfUserHasSessionId('trainer')):
$userDispId = intval($_SESSION['passcodetrainer_id']);
elseif ($isWKL && checkIfUserHasSessionId('wk_leitung')):
$userDispId = intval($_SESSION['passcodewk_leitung_id']);
endif;
$sql = "SELECT `username`, `freigabe` FROM $tableInternUsers WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param('i', $userDispId);
$stmt->execute();
$stmt->bind_result($usernameDB, $freigabenDB);
$stmt->fetch();
$username = $usernameDB ?? '';
$decoded = json_decode($freigabenDB, true);
$freigabenSidebar = is_array($decoded) ? $decoded : [];
$stmt->close();
}
$links = [];
$renderMenu = true;
// Trainer & Kampfrichter are visible to their own role + WKL
if ($isTrainer || $isWKL) {
$links[] = ['key' => 'trainer', 'label' => 'Trainer', 'url' => '/intern/trainer', 'freigaben' => true];
}
if ($isKampfrichter || $isWKL) {
$links[] = ['key' => 'kampfrichter', 'label' => 'Kampfrichter', 'url' => '/intern/kampfrichter', 'freigaben' => true];
}
// WKL-only pages
if ($isWKL) {
$links[] = ['key' => 'rechnungen', 'label' => 'Rechnungen', 'url' => '/intern/wk-leitung/rechnungen', 'freigaben' => false];
$links[] = ['key' => 'logindata', 'label' => 'Benutzerverwaltung', 'url' => '/intern/wk-leitung/logindata', 'freigaben' => false];
$links[] = ['key' => 'displaycontrol', 'label' => 'Displaycontrol', 'url' => '/intern/wk-leitung/displaycontrol', 'freigaben' => false];
//$links[] = ['key' => 'kalender', 'label' => 'Kalender', 'url' => '/intern/wk-leitung/kalender'];
$links[] = ['key' => 'riegeneinteilung', 'label' => 'Riegeneinteilung', 'url' => '/intern/wk-leitung/riegeneinteilung', 'freigaben' => false];
$links[] = ['key' => 'einstellungen', 'label' => 'Einstellungen', 'url' => '/intern/wk-leitung/einstellungen', 'freigaben' => false];
}
function checkIfUserHasSessionId($type) : bool {
if (isset($_SESSION['passcode'.$type.'_id']) && intval(['passcode'.$type.'_id']) > 0) { return true; }
else { return false; }
}
function sidebarRender(string $mode) {
global $isWKL, $isTrainer, $isKampfrichter, $links, $currentPage, $icons, $renderMenu, $username, $freigabenSidebar;
if (!$renderMenu) { return; }
if ($mode === 'button') {
?>
<!-- Sidebar Toggle Button -->
<button class="sidebar-toggle" id="sidebar-toggle" aria-label="Navigation öffnen">
<span></span>
<span></span>
<span></span>
</button>
<?php } elseif ($mode === 'modal') { ?>
<!-- Sidebar Overlay -->
<div class="sidebar-overlay" id="sidebar-overlay"></div>
<!-- Sidebar Panel -->
<nav class="sidebar-nav" id="sidebar-nav">
<div class="sidebar-header">
<h3>Navigation</h3>
<button class="sidebar-close-btn" id="sidebar-close" aria-label="Schliessen">
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
<line x1="18" y1="6" x2="6" y2="18"/><line x1="6" y1="6" x2="18" y2="18"/>
</svg>
</button>
</div>
<?php if ($isWKL && ($isTrainer || $isKampfrichter || true)): ?>
<div class="sidebar-section-label">Allgemein</div>
<?php endif; ?>
<ul class="sidebar-links">
<?php foreach ($links as $i => $link):
$isCurrentPage = ($currentPage === $link['key']);
$activeClass = $isCurrentPage ? ' active' : '';
$icon = $icons[$link['key']] ?? '';
$freigbenArrayName = 'freigaben' . ucfirst($link['key']);
// Insert section divider before WKL-only links
if ($isWKL && $link['key'] === 'rechnungen' && $i > 0): ?>
</ul>
<div class="sidebar-section-label">WK-Leitung</div>
<ul class="sidebar-links">
<?php endif; ?>
<li>
<a href="<?php echo $link['url']; ?>" class="<?php echo trim($activeClass); ?>">
<?php echo $icon; ?>
<?php echo htmlspecialchars($link['label']); ?>
</a>
</li>
<?php if ($isCurrentPage && $link['freigaben'] === true && isset($freigabenSidebar[$freigbenArrayName]) && count($freigabenSidebar[$freigbenArrayName]) > 1) : ?>
<?php $selectedFreigabe = $_SESSION['selectedFreigabe' . ucfirst($link['key'])] ?? ''; ?>
<li class="sidebar-li-freigaben">
<label class="sidebar-freigaben-label" for="selectTriggerFreigabe">Freigabe</label>
<div class="customSelect" id="selectedOption" data-value="[]">
<button type="button" id="selectTriggerFreigabe" class="selectTrigger" aria-expanded="false">
<span class="selectLabel"><?= $selectedFreigabe ?></span>
<svg class="selectArrow" xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' height="14" width="14">
<path d='M6 9L12 15L18 9' stroke='currentColor' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/>
</svg>
</button>
<ul class="selectOptions">
<?php foreach ($freigabenSidebar[$freigbenArrayName] as $f) :?>
<?php $selected = ($f === $selectedFreigabe) ? 'selected' : '' ?>
<li data-value="<?= htmlspecialchars($f) ?>" class="<?= $selected ?>"><?= htmlspecialchars($f) ?></li>
<?php endforeach; ?>
</ul>
<input type="hidden" name="type" class="selectValue" id="freigabenSidebarSelect" value="">
</div>
</li>
<?php endif; ?>
<?php endforeach; ?>
</ul>
<?php
if (isset($username)) : ?>
<div class="sidebarUsername">Benutzer: <?= $username ?></div>
<?php endif; ?>
<div class="sidebar-footer">
<form method="POST" action="" class="abmelden"><input class="abmeldenbutton" type="submit" href="?logout=1" name="abmelden" value="Abmelden"></form>
</div>
</nav>
<script>
const siteType = '<?= $currentPage ?>';
// Close button binding (inline to avoid race condition with sidebar.js)
document.addEventListener('DOMContentLoaded', function() {
var closeBtn = document.getElementById('sidebar-close');
if (closeBtn) {
closeBtn.addEventListener('click', function() {
document.getElementById('sidebar-nav').classList.remove('open');
document.getElementById('sidebar-overlay').classList.remove('open');
document.getElementById('sidebar-toggle').classList.remove('open');
localStorage.setItem('intern_sidebar_open', 'false');
});
}
});
</script>
<script src="/intern/js/sidebar.js"></script>
<?php
}
}

View File

@@ -0,0 +1,172 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if ( empty($_SESSION['access_granted_trainer']) || $_SESSION['access_granted_trainer'] !== true || empty($_SESSION['passcodetrainer_id']) || intval($_SESSION['passcodetrainer_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
$type = 'tr';
$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';
$ids = isset($_GET['ids']) ? htmlspecialchars(strip_tags(trim($_GET['ids'])), ENT_QUOTES) : '';
$user = intval($_SESSION['passcodetrainer_id']);
$arrayids = array_filter(array_map('trim', explode(',', $ids)));
if (!$arrayids || !is_array($arrayids) || count($arrayids) === 0) {
http_response_code(422);
exit;
}
if ($user <= 0) {
http_response_code(422);
exit;
}
$placeholders = [];
$values = [];
$types = '';
foreach ($arrayids as $id) {
if ($id <= 0) {
http_response_code(422);
exit;
}
$sql = "SELECT bezahlt FROM $tableTurnerinnen WHERE id = ?";
$checkstmt = $mysqli->prepare($sql);
$checkstmt->bind_param("i", $id);
if (!$checkstmt->execute()) {
http_response_code(500);
exit;
}
$checkstmt->bind_result($bezahlt);
$checkstmt->fetch();
$checkstmt->close();
$sql2 = "SELECT COUNT(*) FROM $tableBasketItems WHERE user_id = ? AND item_id = ?";
$checkstmt2 = $mysqli->prepare($sql2); // ✔ use $sql2
$checkstmt2->bind_param("ii", $user, $id);
if (!$checkstmt2->execute()) {
http_response_code(500);
exit;
}
// Bind the result
$checkstmt2->bind_result($countProd);
$checkstmt2->fetch(); // fetch into $countProd
$checkstmt2->close();
if ((isset($bezahlt) && (intval($bezahlt) === 0 || intval($bezahlt) === 1)) && $countProd === 0) {
$placeholders[] = '(?, ?)';
$types .= 'ii'; // assuming user_id and item_id are integers
$values[] = $user;
$values[] = $id;
}
}
if ($values === [] || $placeholders === [] || $types === '') {
http_response_code(406);
exit;
}
$sql = "INSERT INTO `$tableBasketItems` (user_id, item_id) VALUES " . implode(',', $placeholders);
$stmt = $mysqli->prepare($sql);
$stmt->bind_param($types, ...$values);
$success = $stmt->execute();
$stmt->close();
if (!$success) {
http_response_code(500);
exit;
}
// Prepare statements once
$sSql = "SELECT `name`, `vorname`, `programm`
FROM $tableTurnerinnen
WHERE id = ?";
$pSql = "SELECT preis
FROM $tableProgramme
WHERE programm = ? AND aktiv = 1";
$sStmt = $mysqli->prepare($sSql);
$pStmt = $mysqli->prepare($pSql);
foreach ($arrayids as $id) {
// --- Turnerinnen ---
$sStmt->bind_param("i", $id);
if (!$sStmt->execute()) {
http_response_code(500);
exit;
}
$row = $sStmt->get_result()->fetch_assoc();
if (!$row || !isset($row['programm'])) {
http_response_code(500);
exit;
}
// --- Programme ---
$pStmt->bind_param("s", $row['programm']);
if (!$pStmt->execute()) {
http_response_code(500);
exit;
}
$pRow = $pStmt->get_result()->fetch_assoc();
if (!$pRow || !isset($pRow['preis'])) {
http_response_code(500);
exit;
}
// --- Build response ---
$arrayresponse[$id] = [
'turnerinName' => $row['name'],
'turnerinVorname' => $row['vorname'],
'programm' => $row['programm'],
'preis' => $pRow['preis']
];
}
// Close statements once
$sStmt->close();
$pStmt->close();
http_response_code(200);
echo json_encode([
'success' => true,
'message' => 'Zum Warenkorb hinzugefügt',
'arrayData' => $arrayresponse
]);
exit;

View File

@@ -0,0 +1,46 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if (empty($_SESSION['access_granted_trainer']) || $_SESSION['access_granted_trainer'] !== true || empty($_SESSION['passcodetrainer_id']) || intval($_SESSION['passcodetrainer_id']) < 1 ) {
http_response_code(403);
exit;
}
$userId = $_SESSION['passcodetrainer_id'];
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
$type = 'tr';
$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';
// --- Check for existing open order ---
$sql = "SELECT * FROM `$tableOrders` WHERE user_id = ? AND order_status = 0 LIMIT 1";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
$result = $result->fetch_assoc();
$stmt->close();
if ($result) {
$_SESSION['order_id'] = $result['order_id'];
$_SESSION['order_preis'] = $result['preis'];
}
echo json_encode(['success' => true, 'result' => $result]);
exit;
?>

View File

@@ -0,0 +1,149 @@
<?php
header('Content-Type: application/json');
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$token = isset($_GET['token']) ? $_GET['token'] : '';
/*if ($token !== 'OOlhSGI8CraW6BFmJbj6JFy4sxrYyZ0UxzzbASLhY1sWm0IgqmBXjqqwtqKSvpVFBSBgOFrXHuQLGQj1pxlxj3rlTt1r7kAAWX67dcP'){
echo json_encode(['success' => false, 'message' => '500 Error - Critical Server Error']);
exit;
}*/
if (empty($_SESSION['access_granted_trainer']) || $_SESSION['access_granted_trainer'] !== true || empty($_SESSION['passcodetrainer_id']) || intval($_SESSION['passcodetrainer_id']) < 1 ) {
http_response_code(403);
exit;
}
function generateInvoiceNumber(): int {
return random_int(10000000, 99999999);
}
$orderType = 'Startgebühr';
function createInvoice(mysqli $conn, $tableOrders, $orderType , $preis, $userId, $jsonIds, $order_status): int
{
$maxRetries = 5;
for ($i = 0; $i < $maxRetries; $i++) {
$invoiceNumber = generateInvoiceNumber();
$stmt = $conn->prepare(
"INSERT INTO `$tableOrders` (order_id, order_type, preis, user_id, item_ids, order_status)
VALUES (?, ?, ?, ?, ?, ?)"
);
$stmt->bind_param(
"isdisi",
$invoiceNumber, $orderType, $preis, $userId, $jsonIds, $order_status
);
if ($stmt->execute()) {
return $invoiceNumber; // SUCCESS
}
// Duplicate key error → retry
if ($conn->errno !== 1062) {
throw new RuntimeException(
"Database error ({$conn->errno}): {$conn->error}"
);
}
}
throw new RuntimeException('Failed to generate unique invoice number');
}
$userId = $_SESSION['passcodetrainer_id'];
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
$type = 'tr';
$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';
$sql = "SELECT bi.id AS basket_id, bi.item_id, p.programm AS programm_name, p.preis, bi.id
FROM $tableBasketItems bi
LEFT JOIN $tableTurnerinnen t ON bi.item_id = t.id
LEFT JOIN $tableProgramme p ON p.programm = t.programm
WHERE bi.user_id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $userId);
$stmt->execute();
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
if (!$rows || count($rows) < 1) {
http_response_code(422);
exit;
}
$preis = 0;
foreach ($rows as $r) {
$preis += floatval($r['preis']);
$ids[] = strval($r['item_id']);
}
if ($preis < 0) {
echo json_encode(['success' => false, 'message' => 'Negative price']);
exit;
}
$jsonIds = json_encode($ids);
if (json_last_error() !== JSON_ERROR_NONE) {
echo json_encode(['success' => false, 'message' => 'Invalid JSON']);
exit;
}
// --- Check for existing open order ---
$sql = "SELECT order_id FROM `$tableOrders` WHERE user_id = ? AND order_status = 0 LIMIT 1";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $userId);
$stmt->execute();
$stmt->bind_result($order_id);
if ($stmt->fetch()) {
// --- UPDATE existing order ---
$stmt->close();
$sql = "
UPDATE `$tableOrders`
SET preis = ?, item_ids = ?
WHERE order_id = ?
";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("dsi", $preis, $jsonIds, $order_id);
$stmt->execute();
$_SESSION['order_id'] = $order_id;
$_SESSION['order_preis'] = $preis;
} else {
// --- INSERT new order ---
$stmt->close();
$order_status = 0;
$new_id = createInvoice($mysqli, $tableOrders, $orderType, $preis, $userId, $jsonIds, $order_status);
$_SESSION['order_id'] = $new_id;
$_SESSION['order_preis'] = $preis;
}
echo json_encode(['success' => true]);
exit;
?>

View File

@@ -0,0 +1,47 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
if ( empty($_SESSION['access_granted_trainer']) || $_SESSION['access_granted_trainer'] !== true || empty($_SESSION['passcodetrainer_id']) || intval($_SESSION['passcodetrainer_id']) < 1 ) {
http_response_code(403);
exit;
}
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
$type = 'tr';
$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';
// --- Get input ---
$id = isset($_POST['id']) ? intval($_POST['id']) : 0;
$userId = intval($_SESSION['passcodetrainer_id']);
// --- Validate inputs ---
if ($id < 1) {
http_response_code(422);
exit;
}
// --- Check for existing open order ---
$sql = "DELETE FROM $tableBasketItems WHERE user_id = ? AND item_id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ii", $userId, $id);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
http_response_code(200);
exit;
?>

View File

@@ -0,0 +1,83 @@
<?php
header('Content-Type: application/json');
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$isTrainer =
isset($_SESSION['access_granted_trainer'], $_SESSION['passcodetrainer_id']) &&
$_SESSION['access_granted_trainer'] === true &&
(int)$_SESSION['passcodetrainer_id'] > 0;
if (!$isTrainer) {
echo json_encode(['success' => false]);
http_response_code(403);
exit;
}
if (!isset($_POST['musicId']) || !isset($_POST['turnerinId']) || intval($_POST['musicId']) < 1 || intval($_POST['turnerinId']) < 1) {
echo json_encode(['success' => false]);
http_response_code(422);
exit;
}
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
$type = 'tr';
$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';
$musicId = intval($_POST['musicId']);
$turnerinId = intval($_POST['turnerinId']);
$sql = "UPDATE $tableTurnerinnen SET bodenmusik = ? WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("ss", $musicId, $turnerinId);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$stmt->close();
$sql = "SELECT name, vorname FROM $tableTurnerinnen WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("s", $turnerinId);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$result = $stmt->get_result();
$rows = $result->fetch_all(MYSQLI_ASSOC);
$stmt->close();
$_SESSION['form_message_type'] = 1;
if (!isset($rows) || !is_array($rows) || count($rows) !== 1) {
$_SESSION['form_message'] = 'Musik aktualisiert';
} else {
$_SESSION['form_message'] = 'Musik für '.$rows[0]['name'].' '.$rows[0]['vorname'].' aktualisiert';
}
$mysqli->close();
echo json_encode(['success' => true]);

View File

@@ -0,0 +1,59 @@
<?php
if (session_status() !== PHP_SESSION_ACTIVE) session_start();
$isTrainer =
isset($_SESSION['access_granted_trainer'], $_SESSION['passcodetrainer_id']) &&
$_SESSION['access_granted_trainer'] === true &&
(int)$_SESSION['passcodetrainer_id'] > 0;
if (!$isTrainer) {
echo json_encode(['success' => false]);
http_response_code(403);
exit;
}
if (!isset($_POST['oldMusicId'])) {
echo json_encode(['success' => false]);
http_response_code(422);
exit;
}
$oldMusicId = intval($_POST['oldMusicId']);
if ($oldMusicId < 1) {
echo json_encode(['success' => true]);
http_response_code(202);
exit;
}
if (!isset($baseDir)) $baseDir = $_SERVER['DOCUMENT_ROOT'];
$type = 'tr';
$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';
$sql = "DELETE $tableAudiofiles WHERE id = ?";
$stmt = $mysqli->prepare($sql);
$stmt->bind_param("i", $oldMusicId);
if (!$stmt->execute()) {
http_response_code(500);
exit;
}
$stmt->close();
$mysqli->close();
echo json_encode(['success' => true]);
http_response_code(200);
exit;

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