Compare commits

..

14 Commits

Author SHA1 Message Date
4ab28370bc Update KC Wahlen Button Reihenfolge
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-02-26 22:58:11 +00:00
c56aa70617 Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2m29s
2026-02-19 09:50:08 +00:00
21553050f8 Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2m30s
2026-02-19 08:43:37 +00:00
f18664251e Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 2m31s
update Readme
2026-02-19 08:35:05 +00:00
1a090dd8dc Update README.md
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Has been cancelled
2026-02-19 08:23:53 +00:00
82f2af7e7a Update install.php
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 15s
2026-02-06 18:45:45 +00:00
bbfa923c9d Merge pull request 'update deploy workflow' (#6) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 16s
Reviewed-on: #6
2026-02-06 18:43:57 +00:00
ProgrammGamer
58c70908c6 update deploy workflow
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 15s
2026-02-06 19:42:40 +01:00
8827e5b324 Merge pull request 'develop' (#5) from develop into main
Some checks failed
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Failing after 4s
Reviewed-on: #5
2026-02-06 18:41:46 +00:00
ProgrammGamer
a65651358a upstream develop
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 33s
2026-02-06 19:40:17 +01:00
f1fb4cd4ea Merge pull request 'develop' (#4) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 13s
Reviewed-on: #4
2026-01-31 13:26:30 +00:00
a827275995 Merge pull request 'develop loesung zu Lösung' (#3) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 14s
Reviewed-on: #3
2026-01-31 10:56:48 +00:00
cd5a0f72de Merge pull request 'frontend: hide results when wahl is freigegeben in shortcode' (#2) from to-main/frontend-ergebnis-hide-results into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
Reviewed-on: #2
2026-01-31 09:56:01 +00:00
8029ebfb27 Merge pull request 'develop' (#1) from develop into main
All checks were successful
Deploy Workshop-Wahlen (DEV / PROD) / deploy (push) Successful in 12s
Reviewed-on: #1
2026-01-30 21:54:02 +00:00
4 changed files with 166 additions and 180 deletions

View File

@@ -35,6 +35,7 @@ jobs:
# ===================== # =====================
- name: Deploy to DEV via FTP - name: Deploy to DEV via FTP
if: github.ref == 'refs/heads/develop' if: github.ref == 'refs/heads/develop'
shell: bash
run: | run: |
apt-get update apt-get update
apt-get install -y lftp apt-get install -y lftp
@@ -51,25 +52,26 @@ jobs:
# ===================== # =====================
# PROD DEPLOY (FTP) # PROD DEPLOY (FTP)
# ===================== # =====================
- name: Deploy to PROD via Docker (SSH) - name: Deploy to PROD via SSH
if: github.ref == 'refs/heads/main' if: github.ref == 'refs/heads/main'
env: env:
SSH_HOST: ${{ secrets.PROD_SSH_HOST }} SSH_HOST: ${{ secrets.PROD_SSH_HOST }}
SSH_USER: ${{ secrets.PROD_SSH_USER }} SSH_USER: ${{ secrets.PROD_SSH_USER }}
SSH_PORT: ${{ secrets.PROD_SSH_PORT }} SSH_PORT: ${{ secrets.PROD_SSH_PORT }}
SSH_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }} SSH_KEY: ${{ secrets.PROD_SSH_PRIVATE_KEY }}
WP_CONTAINER: ${{ secrets.PROD_WP_CONTAINER }} HOST_WP_ROOT: ${{ secrets.PROD_HOST_WP_ROOT }}
WP_PLUGIN_PATH: ${{ secrets.PROD_WP_PLUGIN_PATH }} HOST_ABS_PLUGIN_PATH: ${{ secrets.PROD_HOST_ABS_PLUGIN_PATH }}
HOST_PLUGIN_PATH: ${{ secrets.PROD_HOST_PLUGIN_PATH }} shell: bash
run: | run: |
set -euo pipefail set -euo pipefail
apt-get update apt-get update
# docker-cli needed to talk to remote engine; openssh-client to auth; tar for packaging with excludes # openssh-client to auth; tar for packaging with excludes
apt-get install -y docker.io openssh-client tar apt-get install -y openssh-client tar
# Prepare SSH key # Prepare SSH key
mkdir -p ~/.ssh mkdir -p ~/.ssh
echo "$SSH_KEY" > ~/.ssh/prod_docker_key # Write the multiline private key exactly as provided
printf "%s\n" "$SSH_KEY" > ~/.ssh/prod_docker_key
chmod 600 ~/.ssh/prod_docker_key chmod 600 ~/.ssh/prod_docker_key
# Write SSH config so DOCKER_HOST can reference a named host # Write SSH config so DOCKER_HOST can reference a named host
@@ -79,22 +81,14 @@ jobs:
User ${SSH_USER} User ${SSH_USER}
Port ${SSH_PORT} Port ${SSH_PORT}
IdentityFile ~/.ssh/prod_docker_key IdentityFile ~/.ssh/prod_docker_key
IdentitiesOnly yes
PubkeyAuthentication yes
StrictHostKeyChecking accept-new StrictHostKeyChecking accept-new
CFG CFG
# Prime known_hosts (optional, StrictHostKeyChecking accept-new will handle first connect) # Prime known_hosts (optional, StrictHostKeyChecking accept-new will handle first connect)
ssh-keyscan -p "$SSH_PORT" "$SSH_HOST" >> ~/.ssh/known_hosts || true ssh-keyscan -p "$SSH_PORT" "$SSH_HOST" >> ~/.ssh/known_hosts || true
# Point docker CLI to remote engine over SSH
export DOCKER_HOST=ssh://prod-docker
# Sanity checks
docker info > /dev/null
docker ps --format 'table {{.Names}}\t{{.Status}}'
# Defaults if path not provided
: "${WP_PLUGIN_PATH:=/var/www/html/wp-content/plugins/konficastle-workshopwahl}"
# Create a tarball with excludes to avoid copying VCS and CI folders # Create a tarball with excludes to avoid copying VCS and CI folders
TAR_FILE="/tmp/kc-plugin.tar.gz" TAR_FILE="/tmp/kc-plugin.tar.gz"
tar -czf "$TAR_FILE" \ tar -czf "$TAR_FILE" \
@@ -107,23 +101,19 @@ jobs:
--exclude "*.md" \ --exclude "*.md" \
. .
if [ -n "${HOST_PLUGIN_PATH}" ]; then # Choose target path: prefer absolute plugin path; otherwise derive from HOST_WP_ROOT
echo "Deploying to host path relative to root's home: ~/${HOST_PLUGIN_PATH}" if [ -n "${HOST_ABS_PLUGIN_PATH}" ]; then
# Upload tarball to host home and extract to ~/${HOST_PLUGIN_PATH} echo "Deploying to absolute host path: ${HOST_ABS_PLUGIN_PATH}"
scp -o StrictHostKeyChecking=accept-new -i ~/.ssh/prod_docker_key "$TAR_FILE" prod-docker:~/kc-plugin.tar.gz REMOTE_PATH_DECL="PLUGIN_PATH=\"${HOST_ABS_PLUGIN_PATH%/}\""
ssh prod-docker "mkdir -p \"~/${HOST_PLUGIN_PATH}\" && rm -rf \"~/${HOST_PLUGIN_PATH}\"/* && tar -xzf \"~/kc-plugin.tar.gz\" -C \"~/${HOST_PLUGIN_PATH}\" && rm -f \"~/kc-plugin.tar.gz\"" elif [ -n "${HOST_WP_ROOT}" ]; then
# Verify on host echo "Deploying to host path relative to remote home: \$HOME/${HOST_WP_ROOT%/}/wp-content/plugins/konficastle-workshopwahl"
ssh prod-docker "ls -la \"~/${HOST_PLUGIN_PATH}\" | head -n 50" REMOTE_PATH_DECL="PLUGIN_PATH=\$HOME/${HOST_WP_ROOT%/}/wp-content/plugins/konficastle-workshopwahl"
else else
echo "Deploying directly into container plugin path" echo "Error: set PROD_HOST_ABS_PLUGIN_PATH (absolute) or PROD_HOST_WP_ROOT (relative)" >&2
# Defaults if path not provided (container path) exit 1
: "${WP_PLUGIN_PATH:=/var/www/html/wp-content/plugins/konficastle-workshopwahl}"
# Copy tarball into the container
docker cp "$TAR_FILE" "${WP_CONTAINER}:/tmp/kc-plugin.tar.gz"
# Replace plugin contents inside the container and clean up
docker exec "${WP_CONTAINER}" bash -lc "mkdir -p '${WP_PLUGIN_PATH}' && rm -rf '${WP_PLUGIN_PATH}'/* && tar -xzf /tmp/kc-plugin.tar.gz -C '${WP_PLUGIN_PATH}' && rm -f /tmp/kc-plugin.tar.gz"
# List deployed files for verification inside container
docker exec "${WP_CONTAINER}" bash -lc "ls -la '${WP_PLUGIN_PATH}' | head -n 50"
fi fi
# Upload tarball to host home and extract
scp -o StrictHostKeyChecking=accept-new -o IdentitiesOnly=yes -i ~/.ssh/prod_docker_key "$TAR_FILE" prod-docker:~/kc-plugin.tar.gz
# Use \$HOME for remote expansion (tilde does not expand inside quotes)
ssh prod-docker "$REMOTE_PATH_DECL; mkdir -p \"\$PLUGIN_PATH\" && rm -rf \"\$PLUGIN_PATH\"/* && tar -xzf \"\$HOME/kc-plugin.tar.gz\" -C \"\$PLUGIN_PATH\" && rm -f \"\$HOME/kc-plugin.tar.gz\" && ls -la \"\$PLUGIN_PATH\" | head -n 50"

View File

@@ -47,8 +47,3 @@ Dieses WordPress-Plugin ermöglicht die Verwaltung und Durchführung von Worksho
--- ---
Für Detailfragen zu Datenbankstruktur, Shortcodes oder Zuteilungslogik siehe die jeweiligen Dateien im `includes/`-Verzeichnis. Für Detailfragen zu Datenbankstruktur, Shortcodes oder Zuteilungslogik siehe die jeweiligen Dateien im `includes/`-Verzeichnis.
## Release Notes
Siehe die ausführlichen Hinweise zur Version 1.0 in [RELEASE_NOTES.md](RELEASE_NOTES.md).

View File

@@ -360,9 +360,9 @@ function kc_wahlen_page() {
echo '<td class="kc-actions">'; echo '<td class="kc-actions">';
echo '<a class="kc-btn" href="?page=kc_wahlen&zuweisen='.intval($wahl->id).'">Workshops zuweisen</a>'; echo '<a class="kc-btn" href="?page=kc_wahlen&zuweisen='.intval($wahl->id).'">Workshops zuweisen</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&edit_wahl='.intval($wahl->id).'">Bearbeiten</a>'; echo '<a class="kc-btn" href="?page=kc_wahlen&edit_wahl='.intval($wahl->id).'">Bearbeiten</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&show_zuteilung='.intval($wahl->id).'">Zuteilung anzeigen</a>';
$nonce = wp_create_nonce('kc_run_zuteilung_' . intval($wahl->id)); $nonce = wp_create_nonce('kc_run_zuteilung_' . intval($wahl->id));
echo '<a class="kc-btn" href="?page=kc_wahlen&run_zuteilung='.intval($wahl->id).'&_wpnonce='.$nonce.'" onclick="return confirm(\'Zuteilung wirklich starten? Dies überschreibt vorhandene Zuteilungen.\');">Zuteilung starten</a>'; echo '<a class="kc-btn" href="?page=kc_wahlen&run_zuteilung='.intval($wahl->id).'&_wpnonce='.$nonce.'" onclick="return confirm(\'Zuteilung wirklich starten? Dies überschreibt vorhandene Zuteilungen.\');">Zuteilung starten</a>';
echo '<a class="kc-btn" href="?page=kc_wahlen&show_zuteilung='.intval($wahl->id).'">Zuteilung anzeigen</a>';
echo '<a href="?page=kc_wahlen&delete_wahl='.intval($wahl->id).'" class="kc-btn del" onclick="return confirm(\'Wirklich löschen?\');">Löschen</a>'; echo '<a href="?page=kc_wahlen&delete_wahl='.intval($wahl->id).'" class="kc-btn del" onclick="return confirm(\'Wirklich löschen?\');">Löschen</a>';
echo '</td>'; echo '</td>';
echo '</tr>'; echo '</tr>';

View File

@@ -1,139 +1,140 @@
<?php <?php
if (!defined('ABSPATH')) exit; if (!defined('ABSPATH')) exit;
/** /**
* Create plugin database tables on activation and remove them on deactivation. * Create plugin database tables on activation and remove them on deactivation.
*/
function kc_install_tables() { */
global $wpdb; function kc_install_tables() {
$prefix = $wpdb->prefix; global $wpdb;
$charset_collate = $wpdb->get_charset_collate(); $prefix = $wpdb->prefix;
$charset_collate = $wpdb->get_charset_collate();
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
// Tables definitions
$tables_sql = []; // Tables definitions
$tables_sql = [];
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahlen (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_wahlen (
name varchar(191) NOT NULL, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
beschreibung text DEFAULT NULL, name varchar(191) NOT NULL,
anzahl_einheiten tinyint NOT NULL DEFAULT 1, beschreibung text DEFAULT NULL,
min_kapazitaet int NOT NULL DEFAULT 0, anzahl_einheiten tinyint NOT NULL DEFAULT 1,
max_kapazitaet int NOT NULL DEFAULT 0, min_kapazitaet int NOT NULL DEFAULT 0,
freigegeben tinyint(1) NOT NULL DEFAULT 0, max_kapazitaet int NOT NULL DEFAULT 0,
deleted tinyint(1) NOT NULL DEFAULT 0, freigegeben tinyint(1) NOT NULL DEFAULT 0,
PRIMARY KEY (id) deleted tinyint(1) NOT NULL DEFAULT 0,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_workshops (
name varchar(191) NOT NULL, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
beschreibung text DEFAULT NULL, name varchar(191) NOT NULL,
max_teilnehmer int NOT NULL DEFAULT 0, beschreibung text DEFAULT NULL,
min_teilnehmer int NOT NULL DEFAULT 0, max_teilnehmer int NOT NULL DEFAULT 0,
PRIMARY KEY (id) min_teilnehmer int NOT NULL DEFAULT 0,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_teamer (
vorname varchar(191) DEFAULT NULL, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
nachname varchar(191) DEFAULT NULL, vorname varchar(191) DEFAULT NULL,
PRIMARY KEY (id) nachname varchar(191) DEFAULT NULL,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_teilnehmer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_teilnehmer (
vorname varchar(191) DEFAULT NULL, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
nachname varchar(191) DEFAULT NULL, vorname varchar(191) DEFAULT NULL,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0, nachname varchar(191) DEFAULT NULL,
phase tinyint NOT NULL DEFAULT 1, wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
wunsch1 bigint(20) unsigned DEFAULT NULL, phase tinyint NOT NULL DEFAULT 1,
wunsch2 bigint(20) unsigned DEFAULT NULL, wunsch1 bigint(20) unsigned DEFAULT NULL,
wunsch3 bigint(20) unsigned DEFAULT NULL, wunsch2 bigint(20) unsigned DEFAULT NULL,
deleted tinyint(1) NOT NULL DEFAULT 0, wunsch3 bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id) deleted tinyint(1) NOT NULL DEFAULT 0,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_wahl_workshops (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_wahl_workshops (
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
workshop_id bigint(20) unsigned NOT NULL DEFAULT 0, wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1, workshop_id bigint(20) unsigned NOT NULL DEFAULT 0,
PRIMARY KEY (id) phase tinyint NOT NULL DEFAULT 1,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_force_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_force_zuteilung (
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0, teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
phase tinyint NOT NULL DEFAULT 1, wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
workshop_id bigint(20) unsigned DEFAULT NULL, phase tinyint NOT NULL DEFAULT 1,
kommentar text DEFAULT NULL, workshop_id bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id) kommentar text DEFAULT NULL,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_zuteilung (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_zuteilung (
teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
vorname varchar(191) DEFAULT NULL, teilnehmer_id bigint(20) unsigned NOT NULL DEFAULT 0,
nachname varchar(191) DEFAULT NULL, vorname varchar(191) DEFAULT NULL,
wahl_id bigint(20) unsigned NOT NULL DEFAULT 0, nachname varchar(191) DEFAULT NULL,
phase tinyint NOT NULL DEFAULT 1, wahl_id bigint(20) unsigned NOT NULL DEFAULT 0,
workshop_id bigint(20) unsigned DEFAULT NULL, phase tinyint NOT NULL DEFAULT 1,
wunsch_rang tinyint DEFAULT NULL, workshop_id bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id) wunsch_rang tinyint DEFAULT NULL,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
$tables_sql[] = "CREATE TABLE {$prefix}kc_workshop_teamer (
id bigint(20) unsigned NOT NULL AUTO_INCREMENT, $tables_sql[] = "CREATE TABLE {$prefix}kc_workshop_teamer (
workshop_id bigint(20) unsigned DEFAULT NULL, id bigint(20) unsigned NOT NULL AUTO_INCREMENT,
teamer_id bigint(20) unsigned DEFAULT NULL, workshop_id bigint(20) unsigned DEFAULT NULL,
PRIMARY KEY (id) teamer_id bigint(20) unsigned DEFAULT NULL,
) $charset_collate"; PRIMARY KEY (id)
) $charset_collate";
foreach ($tables_sql as $sql) {
dbDelta($sql); foreach ($tables_sql as $sql) {
} dbDelta($sql);
}
// Post-creation migration guard: ensure min_teilnehmer exists for older installs
$col = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer')); // Post-creation migration guard: ensure min_teilnehmer exists for older installs
if (empty($col)) { $col = $wpdb->get_var($wpdb->prepare("SHOW COLUMNS FROM {$prefix}kc_workshops LIKE %s", 'min_teilnehmer'));
// try to add the column (no-op on newer installs) if (empty($col)) {
$wpdb->query("ALTER TABLE {$prefix}kc_workshops ADD COLUMN min_teilnehmer INT NOT NULL DEFAULT 0"); // try to add the column (no-op on newer installs)
} $wpdb->query("ALTER TABLE {$prefix}kc_workshops ADD COLUMN min_teilnehmer INT NOT NULL DEFAULT 0");
}
// Optionally store plugin version
add_option('kc_workshopwahl_db_version', '1.0'); // Optionally store plugin version
} add_option('kc_workshopwahl_db_version', '1.0');
}
function kc_uninstall_tables() {
global $wpdb; function kc_uninstall_tables() {
$prefix = $wpdb->prefix; global $wpdb;
$prefix = $wpdb->prefix;
$tables = [
"{$prefix}kc_zuteilung", $tables = [
"{$prefix}kc_force_zuteilung", "{$prefix}kc_zuteilung",
"{$prefix}kc_wahl_workshops", "{$prefix}kc_force_zuteilung",
"{$prefix}kc_teilnehmer", "{$prefix}kc_wahl_workshops",
"{$prefix}kc_teamer", "{$prefix}kc_teilnehmer",
"{$prefix}kc_workshops", "{$prefix}kc_teamer",
"{$prefix}kc_wahlen", "{$prefix}kc_workshops",
"{$prefix}kc_workshop_teamer" "{$prefix}kc_wahlen",
]; "{$prefix}kc_workshop_teamer"
];
foreach ($tables as $t) {
$wpdb->query("DROP TABLE IF EXISTS $t"); foreach ($tables as $t) {
} $wpdb->query("DROP TABLE IF EXISTS $t");
}
delete_option('kc_workshopwahl_db_version');
} delete_option('kc_workshopwahl_db_version');
}
// Backwards-compat wrapper for register_activation_hook usage
if (!function_exists('kc_register_hooks_internal')) { // Backwards-compat wrapper for register_activation_hook usage
function kc_register_hooks_internal() { if (!function_exists('kc_register_hooks_internal')) {
// intentionally left blank function kc_register_hooks_internal() {
} // intentionally left blank
} }
}
?>
?>