16 Commits
1.0 ... main

Author SHA1 Message Date
14bd0bfc68 README.md aktualisiert 2025-11-26 16:43:00 +00:00
47543ed98d README.md aktualisiert 2025-11-26 16:42:36 +00:00
aa58ae479c Dateien nach "assets/js" hochladen 2025-11-26 16:37:33 +00:00
a815fe7c7b Dateien nach "assets/css" hochladen 2025-11-26 16:37:18 +00:00
525616391a Dateien nach "includes" hochladen 2025-11-26 16:36:49 +00:00
8f82a72ea3 Dateien nach "templates" hochladen 2025-11-26 16:36:26 +00:00
2318594ea9 Dateien nach "/" hochladen 2025-11-26 16:35:57 +00:00
b934c94765 wp-multi-team-card.php gelöscht 2025-11-26 16:35:14 +00:00
023a2818cb teamcard-admin.js gelöscht 2025-11-26 16:35:11 +00:00
b67644318e teamcard-admin.css gelöscht 2025-11-26 16:35:06 +00:00
35c89990f3 README.md aktualisiert 2025-04-20 16:11:14 +00:00
415d717c78 README.md aktualisiert 2025-04-20 16:10:26 +00:00
da9a6af766 README.md aktualisiert 2025-04-20 16:09:05 +00:00
7beed53f66 README.md aktualisiert 2025-04-20 16:07:11 +00:00
a799012952 wp-multi-team-card.php aktualisiert 2025-04-20 11:21:21 +00:00
193119cc2d wp-multi-team-card.php aktualisiert 2025-04-20 11:20:16 +00:00
17 changed files with 1273 additions and 766 deletions

103
README.md
View File

@@ -1,23 +1,100 @@
# WP Multi Team-Card
## Einstellungen im Backend **Erstellt Teamkarten mit Name, Funktion, Zuständigkeit, Bild und Kategorie. Ausgabe per Shortcode `[teamcards]`.**
Im WordPress-Adminbereich kannst du Teammitglieder hinzufügen, bearbeiten und löschen. Jedes Teammitglied besteht aus den folgenden Feldern: [![Letzte Version](https://img.shields.io/badge/Version-2.0-blue)](https://git.viper.ipv64.net/M_Viper/wp-multi-teamcard/releases)
[Telegram Support](https://t.me/M_Viper04)
- **Name** ---
- **Funktion**
- **Zuständigkeit**
- **Bild**
- **Kategorie**
## Entwickeln ## Features
Wenn du Änderungen am Plugin vornehmen möchtest, kannst du das Plugin nach Belieben anpassen. Achte darauf, dass du die richtigen Hooks und Filter verwendest, um deine Anpassungen mit zukünftigen Versionen kompatibel zu halten. - Verwaltung von Teammitgliedern im WordPress-Backend
- Teamkarten mit Name, Funktion, Zuständigkeit, Bild und Kategorie
- Drag & Drop Sortierung und Inline-Bearbeitung im Backend
- Responsive Darstellung im Frontend
- Ausgabe per Shortcode `[teamcards]`
- Kategorien für Teammitglieder
- Bild-Upload und Vorschau
- Automatische Update-Benachrichtigung bei neuen Releases
## Changelog ---
### Version 1.0 ## Installation
- Erste Veröffentlichung des Plugins.
1. Lade das Plugin herunter oder klone das Repository: git clone https://git.viper.ipv64.net/M_Viper/wp-multi-teamcard.git
2. Kopiere den Ordner `wp-multi-teamcard` in dein WordPress-Plugin-Verzeichnis (`/wp-content/plugins/`).
3. Aktiviere das Plugin im WordPress-Backend unter „Plugins“.
**Mindestanforderungen:**
- WordPress 6.7.2 oder neuer
- PHP 7.2 oder neuer
---
## Nutzung
### Teammitglieder verwalten
Im WordPress-Backend erscheint ein Menüpunkt **Teamkarten**. Dort kannst du:
- Neue Teammitglieder hinzufügen (Name, Funktion, Zuständigkeit, Bild, Kategorie)
- Bestehende Teammitglieder bearbeiten (per Klick auf das jeweilige Feld)
- Reihenfolge per Drag & Drop ändern
- Teammitglieder löschen
- Bilder per Medienauswahl zuweisen oder ändern
### Shortcode
Gib den Shortcode überall im Editor ein, wo die Teamkarten angezeigt werden sollen: `[teamcards]`
**Optional:** Nach Kategorie filtern: `[teamcards kategorie="support"]`
Ersetze `"support"` durch den Slug der gewünschten Kategorie.
---
## Beispielausgabe (Frontend)
Die Teamkarten werden als responsive Grid angezeigt, z.B.:
+-------------------+----------------------------------+ </br>
| [Bild] | Name: Max Mustermann | </br>
| | Funktion: Support | </br>
| | Zuständigkeit: Kundenservice | </br>
+-------------------+----------------------------------+ </br>
---
## Entwickler-Infos
- **Custom Post Type:** `teamcard`
- **Taxonomie:** `teamcard_kategorie`
- **Shortcode:** `[teamcards]`
- **AJAX-Handler:** Hinzufügen, Bearbeiten, Löschen, Sortieren, Bild-Upload
- **Styles & Scripts:** Werden nur im Admin geladen
---
## Update-Hinweis
Das Plugin prüft automatisch, ob eine neue Version auf Gitea verfügbar ist, und zeigt einen Hinweis im Admin-Bereich an.
---
## Support
- [Telegram Support](https://t.me/M_Viper04)
---
## Lizenz ## Lizenz
Dieses Plugin wird unter der GPL2-Lizenz veröffentlicht. Weitere Informationen findest du unter [GPL2 Lizenz](https://www.gnu.org/licenses/gpl-2.0.html). GPLv2 siehe [LICENSE](https://www.gnu.org/licenses/gpl-2.0.html)
---
## Autor
- **M_Viper**
- [m-viper.de](https://m-viper.de)

View File

@@ -0,0 +1,102 @@
/* Allgemeine Admin-Stile */
.teamcard-admin .nav-tab-wrapper {
margin-bottom: 20px;
}
.teamcard-admin .tab-content {
background: #fff;
padding: 20px;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
}
.teamcard-add-new-form {
background: #f9f9f9;
border: 1px solid #e5e5e5;
padding: 20px;
margin-bottom: 20px;
}
.form-fields {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
}
.form-field label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.form-field input[type="text"],
.form-field select {
width: 100%;
padding: 8px;
}
.teamcard-table .column-thumbnail {
width: 120px;
}
.teamcard-table td {
vertical-align: middle;
}
.image-preview-container {
margin-bottom: 10px;
min-height: 50px;
}
.teamcard-bild-vorschau {
max-width: 80px;
max-height: 80px;
border-radius: 4px;
border: 1px solid #ddd;
}
.editable {
padding: 8px;
border: 1px solid transparent;
border-radius: 4px;
transition: all 0.2s ease-in-out;
min-height: 18px; /* Verhindert Sprünge */
}
.editable:hover {
border-color: #0073aa;
background-color: #f3f3f3;
cursor: pointer;
}
.inline-edit {
width: 100%;
padding: 6px;
box-sizing: border-box;
}
.ui-sortable-helper {
background-color: #fff;
box-shadow: 0 2px 5px rgba(0,0,0,0.2);
}
.ui-state-highlight {
height: 80px;
background-color: #f0f0f0;
border: 1px dashed #ccc;
display: table-cell;
vertical-align: middle;
text-align: center;
color: #999;
}
.ui-state-highlight::before {
content: "Hier ablegen";
}
#teamcard-message {
position: fixed;
top: 50px;
right: 20px;
z-index: 9999;
max-width: 300px;
}

View File

@@ -0,0 +1,233 @@
/* Grid-Container für die Teamkarten */
.teamcard-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: flex-start;
margin: 30px 0;
}
/* Basis-Stile für alle Teamkarten */
.teamcard {
background-color: #fff;
border-radius: 8px;
padding: 20px;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.05);
transition: transform 0.3s ease, box-shadow 0.3s ease;
overflow: hidden; /* Verhindert, dass abgerundete Ecken vom Bild abgeschnitten werden */
}
.teamcard:hover {
transform: translateY(-5px);
box-shadow: 0 8px 16px rgba(0, 0, 0, 0.1);
}
.teamcard-image {
text-align: center;
margin-bottom: 15px;
}
.teamcard-image img {
max-width: 120px;
height: 120px;
object-fit: cover; /* Schneidet Bilder zu, ohne sie zu verzerren */
border-radius: 50%;
border: 3px solid #f0f0f0;
}
.teamcard-content {
text-align: center;
}
.teamcard-name {
font-size: 1.2em;
font-weight: 600;
margin: 0 0 10px 0;
color: #333;
}
.teamcard-function {
font-size: 1em;
font-weight: 500;
color: #0073aa;
margin: 0 0 5px 0;
}
.teamcard-responsibility {
font-size: 0.9em;
color: #666;
margin: 0;
}
/* Spezifische Stile für die Card-Typen */
/* Standard Card */
.teamcard-standard {
display: flex;
flex-direction: column;
align-items: center;
}
/* Compact Card */
.teamcard-compact {
display: flex;
flex-direction: row;
align-items: center;
text-align: left;
}
.teamcard-compact .teamcard-image {
margin-bottom: 0;
margin-right: 15px;
flex-shrink: 0;
}
.teamcard-compact .teamcard-image img {
width: 60px;
height: 60px;
}
.teamcard-compact .teamcard-content {
flex-grow: 1;
}
/* Featured Card */
.teamcard-featured {
border: 2px solid #0073aa;
background: linear-gradient(145deg, #ffffff, #f9fbff);
}
.teamcard-featured .teamcard-image img {
border-color: #0073aa;
}
.teamcard-featured .teamcard-name {
color: #0073aa;
}
/* --- NEUE STILE FÜR DIE KARTENTYPEN --- */
/* Flip Card */
.teamcard-flip-container {
perspective: 1000px; /* Entfernung zum 3D-Effekt */
}
.teamcard-flipper {
position: relative;
width: 100%;
height: 320px; /* Feste Höhe, wichtig für den Flip-Effekt */
transition: transform 0.8s;
transform-style: preserve-3d;
}
.teamcard-flip-container:hover .teamcard-flipper {
transform: rotateY(180deg);
}
.teamcard-flip {
position: absolute;
width: 100%;
height: 100%;
backface-visibility: hidden; /* Rückseite der Elemente verbergen */
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
text-align: center;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
border-radius: 8px;
border: 1px solid #eee;
}
.teamcard-front {
background-color: #fff;
}
.teamcard-back {
background-color: #f8f9fa;
color: #333;
transform: rotateY(180deg);
}
.teamcard-back .teamcard-content {
padding: 20px;
}
.teamcard-back .teamcard-responsibility {
font-size: 0.9em;
line-height: 1.6;
text-align: left;
}
/* Profile Card */
.teamcard-profile {
border-radius: 0;
overflow: hidden;
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
transition: transform 0.3s ease, box-shadow 0.3s ease;
color: #fff;
display: flex;
flex-direction: column;
}
.teamcard-profile-header {
height: 180px;
background-size: cover;
background-position: center;
position: relative;
display: flex;
align-items: flex-end;
}
.teamcard-profile-header::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: linear-gradient(to top, rgba(0,0,0,0.7), rgba(0,0,0,0.1));
}
.teamcard-profile-content {
position: relative;
z-index: 2;
padding: 20px;
width: 100%;
box-sizing: border-box;
}
.teamcard-profile .teamcard-name {
margin: 0;
color: #fff;
font-size: 1.4em;
}
.teamcard-profile .teamcard-function {
margin: 5px 0 0 0;
color: #f0f0f0;
font-weight: 400;
}
.teamcard-profile-body {
padding: 20px;
background-color: #fff;
color: #333;
flex-grow: 1;
}
.teamcard-profile-body .teamcard-responsibility {
margin: 0;
font-size: 0.9em;
line-height: 1.6;
}
/* Responsive Anpassungen */
@media (max-width: 768px) {
.teamcard-grid {
justify-content: center;
}
.teamcard {
flex-basis: 100% !important;
max-width: 320px;
}
/* Anpassung der Höhe für Flip-Cards auf kleinen Bildschirmen */
.teamcard-flipper {
height: 280px;
}
}

171
assets/js/teamcard-admin.js Normal file
View File

@@ -0,0 +1,171 @@
jQuery(document).ready(function($) {
var newTeamcardFrame;
var teamcardFrame;
// Medienbibliothek für neues Teammitglied
$('#new-teamcard-bild-button').on('click', function(e) {
e.preventDefault();
if (newTeamcardFrame) { newTeamcardFrame.open(); return; }
newTeamcardFrame = wp.media({ title: 'Bild auswählen', button: { text: 'Verwenden' }, multiple: false });
newTeamcardFrame.on('select', function() {
var attachment = newTeamcardFrame.state().get('selection').first().toJSON();
$('#new-teamcard-bild-id').val(attachment.id);
$('#new-teamcard-bild-vorschau').attr('src', attachment.url).show();
});
newTeamcardFrame.open();
});
// Medienbibliothek für vorhandene Teammitglieder
$(document).on('click', '.teamcard-bild-button', function(e) {
e.preventDefault();
var button = $(this);
var teamcardId = button.data('id');
if (teamcardFrame) { teamcardFrame.open(); return; }
teamcardFrame = wp.media({ title: 'Bild ändern', button: { text: 'Verwenden' }, multiple: false });
teamcardFrame.on('select', function() {
var attachment = teamcardFrame.state().get('selection').first().toJSON();
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: { action: 'update_teamcard_image', id: teamcardId, bild_id: attachment.id, nonce: teamcard_data.nonce },
success: function(response) {
if (response.success) {
var container = button.siblings('.image-preview-container');
container.html('<img src="' + response.data.bild_url + '" class="teamcard-bild-vorschau">');
showMessage('Bild erfolgreich aktualisiert.', 'success');
}
}
});
});
teamcardFrame.open();
});
// Neues Teammitglied hinzufügen
$('#add-teamcard-button').on('click', function() {
var name = $('#new-teamcard-name').val();
if (!name) { showMessage('Bitte gib einen Namen ein.', 'error'); return; }
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: {
action: 'add_teamcard',
name: name,
funktion: $('#new-teamcard-funktion').val(),
zustaendigkeit: $('#new-teamcard-zustaendigkeit').val(),
card_type: $('#new-teamcard-card-type').val(),
bild_id: $('#new-teamcard-bild-id').val(),
nonce: teamcard_data.nonce
},
success: function(response) {
if (response.success) {
var bildHtml = response.data.bild_url ? '<img src="' + response.data.bild_url + '" class="teamcard-bild-vorschau">' : '';
var newRow = `
<tr data-id="${response.data.id}" class="teamcard-item">
<td class="teamcard-bild-column"><div class="image-preview-container">${bildHtml}</div><button type="button" class="button teamcard-bild-button" data-id="${response.data.id}">Ändern</button></td>
<td><div class="editable" data-field="title" data-id="${response.data.id}">${name}</div></td>
<td><div class="editable" data-field="funktion" data-id="${response.data.id}">${$('#new-teamcard-funktion').val()}</div></td>
<td><div class="editable" data-field="zustaendigkeit" data-id="${response.data.id}">${$('#new-teamcard-zustaendigkeit').val()}</div></td>
<td>${$('#new-teamcard-card-type option:selected').text()}</td>
<td><button type="button" class="button button-small teamcard-delete" data-id="${response.data.id}">Löschen</button></td>
</tr>`;
$('#teamcard-list').append(newRow);
// Formular zurücksetzen
$('#new-teamcard-name, #new-teamcard-funktion, #new-teamcard-zustaendigkeit, #new-teamcard-bild-id').val('');
$('#new-teamcard-bild-vorschau').hide();
$('#new-teamcard-card-type').prop('selectedIndex',0);
showMessage('Teammitglied erfolgreich hinzugefügt.', 'success');
} else {
showMessage(response.data.message || 'Ein unbekannter Fehler ist aufgetreten.', 'error');
}
}
});
});
// Inline-Bearbeitung
$(document).on('click', '.editable', function() {
var element = $(this);
var currentText = element.text().trim();
element.html('<input type="text" class="inline-edit" value="' + currentText + '">');
element.find('input').focus().select();
});
$(document).on('blur keypress', '.inline-edit', function(e) {
if (e.type === 'keypress' && e.which !== 13) return;
if (e.type === 'keypress') { e.preventDefault(); }
var input = $(this);
var element = input.parent();
var newValue = input.val().trim();
var field = element.data('field');
var id = element.data('id');
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: { action: 'update_teamcard', id: id, field: field, value: newValue, nonce: teamcard_data.nonce },
success: function(response) {
if (response.success) {
element.text(newValue);
showMessage('Erfolgreich aktualisiert.', 'success');
} else {
element.text(newValue); // Text trotzdem aktualisieren, auch wenn der Request fehlschlägt
showMessage('Fehler beim Speichern.', 'error');
}
}
});
});
// Teammitglied löschen
$(document).on('click', '.teamcard-delete', function() {
if (!confirm('Möchtest du dieses Teammitglied wirklich löschen?')) { return; }
var button = $(this);
var id = button.data('id');
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: { action: 'delete_teamcard', id: id, nonce: teamcard_data.nonce },
success: function(response) {
if (response.success) {
button.closest('tr').fadeOut(400, function() { $(this).remove(); });
showMessage('Teammitglied erfolgreich gelöscht.', 'success');
} else {
showMessage('Fehler beim Löschen.', 'error');
}
}
});
});
// Drag & Drop Sortierung
$('#teamcard-list').sortable({
handle: 'td:first', // Nur die erste Spalte als Handle
placeholder: 'ui-state-highlight',
tolerance: 'pointer',
update: function(event, ui) {
var order = $(this).sortable('toArray', {attribute: 'data-id'});
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: { action: 'update_teamcard_order', order: order, nonce: teamcard_data.nonce },
success: function(response) {
if (response.success) {
showMessage('Reihenfolge aktualisiert.', 'success');
}
}
});
}
});
function showMessage(message, type) {
var messageElement = $('#teamcard-message');
messageElement.removeClass('notice-success notice-error').addClass('notice-' + type);
messageElement.html('<p>' + message + '</p>').show().delay(3000).fadeOut();
}
});

40
includes/help-tab.php Normal file
View File

@@ -0,0 +1,40 @@
<?php
// Diese Datei wird in der Haupt-Admin-Datei included.
?>
<div class="teamcard-tab-content">
<h2>Hilfe & Dokumentation</h2>
<div class="help-section">
<h3>Shortcode verwenden</h3>
<p>Um alle Teammitglieder anzuzeigen, verwenden Sie:</p>
<code>[teamcards]</code>
<p>Um nur eine bestimmte Kategorie anzuzeigen:</p>
<code>[teamcards kategorie="management"]</code>
<p>Um einen bestimmten Card-Typ zu erzwingen (überschreibt die Einstellung des Mitglieds):</p>
<code>[teamcards type="featured"]</code>
<p>Um die Anzahl der Spalten zu ändern (überschreibt die globale Einstellung):</p>
<code>[teamcards columns="3"]</code>
</div>
<div class="help-section">
<h3>Card-Typen</h3>
<ul>
<li><strong>Standard Card:</strong> Die klassische Card mit Bild oben und Text darunter.</li>
<li><strong>Compact Card:</strong> Eine platzsparende Version, ideal für lange Listen.</li>
<li><strong>Featured Card:</strong> Eine hervorgehobene Card mit zentriertem Layout und besonderem Styling.</li>
<li><strong>Flip Card:</strong> Eine interaktive Card, die beim Hover gedreht wird, um zusätzliche Informationen (z.B. "Über mich") auf der Rückseite zu zeigen.</li>
<li><strong>Profile Card:</strong> Eine Card mit dem Bild als Hintergrund, ideal für ein modernes, visuelles Profil.</li>
</ul>
</div>
<div class="help-section">
<h3>Support</h3>
<p>Für Support und Fragen kontaktieren Sie uns bitte über:</p>
<ul>
<li><a href="https://discord.com/invite/FdRs4BRd8D" target="_blank">Discord Support</a></li>
<li><a href="https://t.me/M_Viper04" target="_blank">Telegram Support</a></li>
</ul>
</div>
</div>

12
includes/settings-tab.php Normal file
View File

@@ -0,0 +1,12 @@
<?php
// Diese Datei wird in der Haupt-Admin-Datei included.
?>
<div class="teamcard-tab-content">
<form action="options.php" method="post">
<?php
settings_fields('teamcard_settings_group');
do_settings_sections('teamcard_settings');
submit_button('Einstellungen speichern');
?>
</form>
</div>

View File

@@ -0,0 +1,93 @@
<?php
// Diese Datei wird in der Haupt-Admin-Datei included.
?>
<div class="teamcard-tab-content">
<p><strong>Hinweis:</strong> Beim Löschen des Plugins werden alle Teammitglieder und zugehörigen Daten unwiderruflich aus der Datenbank entfernt. Bitte erstellen Sie ein Backup, falls Sie die Daten behalten möchten.</p>
<div class="teamcard-add-new-form">
<h2>Neues Teammitglied hinzufügen</h2>
<div class="form-fields">
<div class="form-field">
<label for="new-teamcard-name">Name:</label>
<input type="text" id="new-teamcard-name" placeholder="Name eingeben" required>
</div>
<div class="form-field">
<label for="new-teamcard-funktion">Funktion:</label>
<input type="text" id="new-teamcard-funktion" placeholder="Funktion eingeben">
</div>
<div class="form-field">
<label for="new-teamcard-zustaendigkeit">Zuständigkeit / Über mich:</label>
<textarea id="new-teamcard-zustaendigkeit" placeholder="Zuständigkeit oder Text für die Rückseite der Flip-Card eingeben"></textarea>
</div>
<div class="form-field">
<label for="new-teamcard-card-type">Card-Typ:</label>
<select id="new-teamcard-card-type">
<option value="standard">Standard Card</option>
<option value="compact">Compact Card</option>
<option value="featured">Featured Card</option>
<!-- NEUE OPTIONEN -->
<option value="flip">Flip Card</option>
<option value="profile">Profile Card</option>
</select>
</div>
<div class="form-field">
<label>Bild:</label>
<div class="image-preview-container">
<img id="new-teamcard-bild-vorschau" src="" style="display:none; max-width:100px;">
</div>
<input type="hidden" id="new-teamcard-bild-id" value="">
<button type="button" class="button" id="new-teamcard-bild-button">Bild auswählen</button>
</div>
<div class="form-field">
<button type="button" id="add-teamcard-button" class="button button-primary">Teammitglied hinzufügen</button>
</div>
</div>
</div>
<h2>Vorhandene Teammitglieder</h2>
<p>Ziehe die Zeilen, um die Reihenfolge zu ändern. Klicke auf die Felder, um sie zu bearbeiten.</p>
<table class="wp-list-table widefat fixed striped teamcard-table">
<thead>
<tr>
<th class="column-thumbnail">Bild</th>
<th>Name</th>
<th>Funktion</th>
<th>Zuständigkeit</th>
<th>Typ</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody id="teamcard-list">
<?php
$teamcards = get_posts(['post_type' => 'teamcard', 'posts_per_page' => -1, 'orderby' => 'menu_order', 'order' => 'ASC']);
foreach ($teamcards as $teamcard) {
$funktion = get_post_meta($teamcard->ID, '_teamcard_funktion', true);
$zustaendigkeit = get_post_meta($teamcard->ID, '_teamcard_zustaendigkeit', true);
$bild_id = get_post_meta($teamcard->ID, '_teamcard_bild_id', true);
$card_type = get_post_meta($teamcard->ID, '_teamcard_card_type', true);
$bild_url = $bild_id ? wp_get_attachment_image_url($bild_id, 'thumbnail') : '';
?>
<tr data-id="<?php echo $teamcard->ID; ?>" class="teamcard-item">
<td class="teamcard-bild-column">
<div class="image-preview-container">
<?php if ($bild_url): ?>
<img src="<?php echo esc_url($bild_url); ?>" class="teamcard-bild-vorschau">
<?php endif; ?>
</div>
<button type="button" class="button teamcard-bild-button" data-id="<?php echo $teamcard->ID; ?>">Ändern</button>
</td>
<td><div class="editable" data-field="title" data-id="<?php echo $teamcard->ID; ?>"><?php echo esc_html($teamcard->post_title); ?></div></td>
<td><div class="editable" data-field="funktion" data-id="<?php echo $teamcard->ID; ?>"><?php echo esc_html($funktion); ?></div></td>
<td><div class="editable" data-field="zustaendigkeit" data-id="<?php echo $teamcard->ID; ?>"><?php echo esc_html($zustaendigkeit); ?></div></td>
<td><?php echo esc_html(ucfirst($card_type ?: 'standard')); ?></td>
<td><button type="button" class="button button-small teamcard-delete" data-id="<?php echo $teamcard->ID; ?>">Löschen</button></td>
</tr>
<?php
}
?>
</tbody>
</table>
<div id="teamcard-message" class="notice" style="display:none;"></div>
</div>

376
multi-team-card.php Normal file
View File

@@ -0,0 +1,376 @@
<?php
/**
* Plugin Name: Multi Team-Card
* Plugin URI: https://git.viper.ipv64.net/M_Viper/wp-multi-teamcard
* Description: Erstellt Teamkarten mit Name, Funktion, Zuständigkeit, Bild und Kategorie. Ausgabe per Shortcode [teamcards].
* Version: 2.0
* Author: M_Viper
* Author URI: https://m-viper.de
* Requires at least: 6.8
* Tested up to: 6.8
* PHP Version: 7.4
* License: GPL2
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: multi-team-card
* Tags: team, card, shortcodes, team-management, customizable, responsive
* Support: [Discord Support](https://discord.com/invite/FdRs4BRd8D)
* Support: [Telegram Support](https://t.me/M_Viper04)
*/
if (!defined('ABSPATH')) exit;
// Check if the PHP version is sufficient
if (version_compare(PHP_VERSION, '7.2', '<')) {
die('This plugin requires PHP 7.2 or higher.');
}
// Get the current version of the plugin from the plugin header dynamically
function get_current_plugin_version() {
if (!function_exists('get_plugin_data')) {
require_once(ABSPATH . 'wp-admin/includes/plugin.php');
}
$plugin_data = get_plugin_data(__FILE__);
return $plugin_data['Version'];
}
$gitea_api_url = 'https://git.viper.ipv64.net/api/v1/repos/M_Viper/wp-multi-teamcard/releases/latest';
function check_for_new_release() {
global $gitea_api_url;
$current_version = get_current_plugin_version();
$response = wp_remote_get($gitea_api_url);
if (is_wp_error($response)) {
return;
}
$body = wp_remote_retrieve_body($response);
$release_data = json_decode($body);
if (isset($release_data->tag_name)) {
$latest_version = ltrim($release_data->tag_name, 'v'); // Entfernt 'v' falls vorhanden
if (version_compare($latest_version, $current_version, '>')) {
add_action('admin_notices', function() use ($latest_version) {
echo '<div class="notice notice-info is-dismissible">
<p><strong>⚠️ Neue Version von WP Multi Teamcard verfügbar:</strong> Version ' . esc_html($latest_version) . ' ist jetzt auf Gitea verfügbar. <a href="https://git.viper.ipv64.net/M_Viper/wp-multi-teamcard/releases" target="_blank">Hier klicken, um die neue Version herunterzuladen. ⚠️</a></p>
</div>';
});
}
}
}
add_action('admin_init', 'check_for_new_release');
// CPT registrieren
function teamcard_register_post_type() {
register_post_type('teamcard', [
'labels' => [
'name' => 'Teammitglieder',
'singular_name' => 'Teammitglied',
'add_new' => 'Neues Teammitglied',
'add_new_item' => 'Teammitglied hinzufügen',
'edit_item' => 'Teammitglied bearbeiten',
],
'public' => true,
'show_ui' => false, // Wir nutzen eine eigene Admin-Seite
'show_in_menu' => false,
'menu_icon' => 'dashicons-groups',
'supports' => ['title'],
'has_archive' => false,
'show_in_admin_bar' => false,
]);
register_taxonomy('teamcard_kategorie', 'teamcard', [
'labels' => [
'name' => 'Kategorien',
'singular_name' => 'Kategorie',
],
'hierarchical' => true,
'public' => true,
'show_admin_column' => true,
'show_ui' => true, // Kategorien im Admin-Menü anzeigen
]);
}
add_action('init', 'teamcard_register_post_type');
// Admin-Menü hinzufügen
function teamcard_add_admin_menu() {
add_menu_page(
'Team-Cards verwalten',
'Team-Cards',
'manage_options',
'teamcard_management',
'teamcard_render_admin_page',
'dashicons-groups',
30
);
}
add_action('admin_menu', 'teamcard_add_admin_menu');
// Die Hauptverwaltungsseite mit Tabs
function teamcard_render_admin_page() {
$active_tab = isset($_GET['tab']) ? sanitize_key($_GET['tab']) : 'team_members';
?>
<div class="wrap teamcard-admin">
<h1>Team-Cards verwalten</h1>
<nav class="nav-tab-wrapper">
<a href="?page=teamcard_management&tab=team_members" class="nav-tab <?php echo $active_tab === 'team_members' ? 'nav-tab-active' : ''; ?>">Teammitglieder</a>
<a href="?page=teamcard_management&tab=settings" class="nav-tab <?php echo $active_tab === 'settings' ? 'nav-tab-active' : ''; ?>">Einstellungen</a>
<a href="?page=teamcard_management&tab=help" class="nav-tab <?php echo $active_tab === 'help' ? 'nav-tab-active' : ''; ?>">Hilfe</a>
</nav>
<div class="tab-content">
<?php
switch ($active_tab) {
case 'team_members':
require_once plugin_dir_path(__FILE__) . 'includes/team-members-tab.php';
break;
case 'settings':
require_once plugin_dir_path(__FILE__) . 'includes/settings-tab.php';
break;
case 'help':
require_once plugin_dir_path(__FILE__) . 'includes/help-tab.php';
break;
}
?>
</div>
</div>
<?php
}
// AJAX-Handler zum Speichern der Teammitglieder
function teamcard_ajax_handlers() {
add_action('wp_ajax_add_teamcard', function() {
check_ajax_referer('teamcard_nonce', 'nonce');
$name = sanitize_text_field($_POST['name']);
$funktion = sanitize_text_field($_POST['funktion']);
$zustaendigkeit = sanitize_textarea_field($_POST['zustaendigkeit']); // Textarea erlauben
$bild_id = intval($_POST['bild_id']);
$card_type = sanitize_text_field($_POST['card_type']);
if (empty($name)) {
wp_send_json_error(['message' => 'Bitte gib einen Namen ein.']);
}
$max_order = 0;
$posts = get_posts(['post_type' => 'teamcard', 'posts_per_page' => 1, 'orderby' => 'menu_order', 'order' => 'DESC']);
if (!empty($posts)) {
$max_order = $posts[0]->menu_order;
}
$post_id = wp_insert_post([
'post_type' => 'teamcard',
'post_title' => $name,
'post_status' => 'publish',
'menu_order' => $max_order + 1
]);
if ($post_id) {
update_post_meta($post_id, '_teamcard_funktion', $funktion);
update_post_meta($post_id, '_teamcard_zustaendigkeit', $zustaendigkeit);
update_post_meta($post_id, '_teamcard_card_type', $card_type);
if ($bild_id > 0) {
update_post_meta($post_id, '_teamcard_bild_id', $bild_id);
}
$bild_url = $bild_id ? wp_get_attachment_image_url($bild_id, 'thumbnail') : '';
wp_send_json_success(['id' => $post_id, 'bild_url' => $bild_url]);
} else {
wp_send_json_error(['message' => 'Fehler beim Erstellen des Teammitglieds.']);
}
});
add_action('wp_ajax_update_teamcard', function() {
check_ajax_referer('teamcard_nonce', 'nonce');
$post_id = intval($_POST['id']);
$field = sanitize_text_field($_POST['field']);
$value = sanitize_textarea_field($_POST['value']); // Textarea erlauben
if ($field === 'title') {
wp_update_post(['ID' => $post_id, 'post_title' => $value]);
} else {
update_post_meta($post_id, '_teamcard_' . $field, $value);
}
wp_send_json_success();
});
add_action('wp_ajax_delete_teamcard', function() {
check_ajax_referer('teamcard_nonce', 'nonce');
$post_id = intval($_POST['id']);
if (wp_delete_post($post_id, true)) {
wp_send_json_success();
} else {
wp_send_json_error(['message' => 'Fehler beim Löschen.']);
}
});
add_action('wp_ajax_update_teamcard_image', function() {
check_ajax_referer('teamcard_nonce', 'nonce');
$post_id = intval($_POST['id']);
$bild_id = intval($_POST['bild_id']);
update_post_meta($post_id, '_teamcard_bild_id', $bild_id);
$bild_url = wp_get_attachment_image_url($bild_id, 'thumbnail');
wp_send_json_success(['bild_url' => $bild_url]);
});
add_action('wp_ajax_update_teamcard_order', function() {
check_ajax_referer('teamcard_nonce', 'nonce');
$order = $_POST['order']; // This is an array of IDs
foreach ($order as $position => $post_id) {
wp_update_post(['ID' => intval($post_id), 'menu_order' => $position]);
}
wp_send_json_success();
});
}
add_action('init', 'teamcard_ajax_handlers');
// Admin-Skripte und Styles laden
function teamcard_admin_scripts($hook) {
if ($hook !== 'toplevel_page_teamcard_management') return;
wp_enqueue_media();
wp_enqueue_script('jquery-ui-sortable');
wp_enqueue_style('teamcard-admin-style', plugin_dir_url(__FILE__) . 'assets/css/teamcard-admin.css', [], '2.1');
wp_enqueue_script('teamcard-admin-script', plugin_dir_url(__FILE__) . 'assets/js/teamcard-admin.js', ['jquery', 'jquery-ui-sortable'], '2.1', true);
wp_localize_script('teamcard-admin-script', 'teamcard_data', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('teamcard_nonce')
]);
}
add_action('admin_enqueue_scripts', 'teamcard_admin_scripts');
// Frontend-Styles laden
function teamcard_frontend_styles() {
// Nur laden, wenn der Shortcode auf der Seite ist
global $post;
if (is_a($post, 'WP_Post') && has_shortcode($post->post_content, 'teamcards')) {
wp_enqueue_style('teamcard-frontend-style', plugin_dir_url(__FILE__) . 'assets/css/teamcard-frontend.css', [], '2.1');
}
}
add_action('wp_enqueue_scripts', 'teamcard_frontend_styles');
// Shortcode für die Frontend-Anzeige
function teamcard_shortcode($atts) {
$atts = shortcode_atts([
'kategorie' => '',
'type' => get_option('teamcard_default_card_type', 'standard'),
'columns' => get_option('teamcard_columns', 2),
], $atts, 'teamcards');
$args = [
'post_type' => 'teamcard',
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC',
];
if (!empty($atts['kategorie'])) {
$args['tax_query'] = [[
'taxonomy' => 'teamcard_kategorie',
'field' => 'slug',
'terms' => sanitize_text_field($atts['kategorie']),
]];
}
$query = new WP_Query($args);
if (!$query->have_posts()) {
return '<p>Keine Teammitglieder gefunden.</p>';
}
// Spaltenbreite für das Grid berechnen
$column_style = '';
if ($atts['columns'] > 1) {
$column_width = (100 / $atts['columns']) - 2; // 2% für den gap
$column_style = "style='flex-basis: calc({$column_width}% - 20px);'";
} else {
$column_style = "style='flex-basis: 100%;'";
}
ob_start();
echo '<div class="teamcard-grid teamcard-grid-' . esc_attr($atts['columns']) . '-cols">';
while ($query->have_posts()) {
$query->the_post();
$post_id = get_the_ID();
$meta = [
'funktion' => get_post_meta($post_id, '_teamcard_funktion', true),
'zustaendigkeit' => get_post_meta($post_id, '_teamcard_zustaendigkeit', true),
'bild_id' => get_post_meta($post_id, '_teamcard_bild_id', true),
'card_type' => get_post_meta($post_id, '_teamcard_card_type', true),
];
$meta['bild_url'] = $meta['bild_id'] ? wp_get_attachment_url($meta['bild_id']) : '';
// Card-Typ aus dem Meta-Feld verwenden, falls vorhanden, sonst den aus dem Shortcode
$card_type_to_render = !empty($meta['card_type']) ? $meta['card_type'] : $atts['type'];
// Sicherstellen, dass die Template-Datei existiert
$template_file = plugin_dir_path(__FILE__) . "templates/card-{$card_type_to_render}.php";
if (file_exists($template_file)) {
include $template_file;
} else {
// Fallback auf Standard-Template, falls spezifisches nicht existiert
include plugin_dir_path(__FILE__) . 'templates/card-standard.php';
}
}
echo '</div>';
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode('teamcards', 'teamcard_shortcode');
// Einstellungs-API registrieren
function teamcard_register_settings() {
register_setting('teamcard_settings_group', 'teamcard_default_card_type', ['sanitize_callback' => 'sanitize_text_field']);
register_setting('teamcard_settings_group', 'teamcard_columns', ['sanitize_callback' => 'absint']);
add_settings_section(
'teamcard_settings_section',
'Anzeige-Einstellungen',
'__return_false', // Keine Beschreibung nötig
'teamcard_settings'
);
add_settings_field(
'teamcard_default_card_type',
'Standard Card-Typ',
'teamcard_default_card_type_callback',
'teamcard_settings',
'teamcard_settings_section'
);
add_settings_field(
'teamcard_columns',
'Anzahl der Spalten',
'teamcard_columns_callback',
'teamcard_settings',
'teamcard_settings_section'
);
}
add_action('admin_init', 'teamcard_register_settings');
function teamcard_default_card_type_callback() {
$default_card_type = get_option('teamcard_default_card_type', 'standard');
?>
<select name="teamcard_default_card_type">
<option value="standard" <?php selected($default_card_type, 'standard'); ?>>Standard Card</option>
<option value="compact" <?php selected($default_card_type, 'compact'); ?>>Compact Card</option>
<option value="featured" <?php selected($default_card_type, 'featured'); ?>>Featured Card</option>
<!-- NEUE OPTIONEN -->
<option value="flip" <?php selected($default_card_type, 'flip'); ?>>Flip Card</option>
<option value="profile" <?php selected($default_card_type, 'profile'); ?>>Profile Card</option>
</select>
<p class="description">Dieser Typ wird verwendet, wenn für ein Mitglied kein spezifischer Typ festgelegt ist.</p>
<?php
}
function teamcard_columns_callback() {
$columns = get_option('teamcard_columns', 2);
?>
<input type="number" name="teamcard_columns" value="<?php echo esc_attr($columns); ?>" min="1" max="4" step="1">
<p class="description">Anzahl der Spalten für die Teammitglieder-Anzeige (1-4).</p>
<?php
}

View File

@@ -1,157 +0,0 @@
.teamcard-admin {
margin: 20px 0;
}
.teamcard-add-new-form {
background: #fff;
border: 1px solid #ccd0d4;
box-shadow: 0 1px 1px rgba(0,0,0,.04);
padding: 20px;
margin-bottom: 20px;
}
.form-fields {
display: flex;
flex-wrap: wrap;
gap: 15px;
}
.form-field {
flex: 1 0 200px;
}
.form-field label {
display: block;
margin-bottom: 5px;
font-weight: 600;
}
.form-field input[type="text"] {
width: 100%;
padding: 8px;
}
.teamcard-item td {
vertical-align: middle;
}
.teamcard-bild {
cursor: move;
}
.image-preview-container {
margin-bottom: 10px;
min-height: 50px;
}
.teamcard-bild-vorschau {
max-width: 80px;
max-height: 80px;
border-radius: 4px;
}
.editable {
padding: 8px;
border: 1px solid transparent;
border-radius: 4px;
transition: all 0.2s;
}
.editable:hover {
border-color: #ddd;
background-color: #f9f9f9;
cursor: pointer;
}
.inline-edit {
width: 100%;
padding: 6px;
}
.ui-state-highlight {
height: 80px;
background-color: #f0f0f0;
border: 1px dashed #ccc;
}
#teamcard-message {
position: fixed;
top: 50px;
right: 20px;
z-index: 9999;
max-width: 300px;
}
/* Container für das Teamkarten-Grid */
.teamcard-grid {
display: flex;
flex-wrap: wrap;
gap: 20px;
justify-content: center;
margin-top: 30px;
}
/* Stil für jedes Teammitglied */
.teamcard {
border: 2px solid #e0e0e0;
border-radius: 10px;
padding: 20px;
width: 280px;
text-align: center;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
background-color: #fff;
transition: transform 0.3s ease, box-shadow 0.3s ease;
}
/* Hover-Effekte */
.teamcard:hover {
transform: translateY(-10px);
box-shadow: 0 10px 30px rgba(0, 0, 0, 0.1);
}
/* Bild des Teammitglieds */
.teamcard img {
max-width: 100px;
height: 100px;
border-radius: 50%;
margin-bottom: 15px;
transition: transform 0.3s ease;
}
/* Hover-Effekt für das Bild */
.teamcard:hover img {
transform: scale(1.1);
}
/* Name des Teammitglieds */
.teamcard h3 {
font-size: 1.2em;
font-weight: 600;
margin: 10px 0;
color: #333;
}
/* Funktion und Zuständigkeit des Teammitglieds */
.teamcard p {
font-size: 0.95em;
color: #666;
margin-bottom: 10px;
}
/* Stil für den Hover-Effekt beim Teammitglied */
.teamcard:hover h3,
.teamcard:hover p {
color: #0073e6;
}
/* Stil für den Container der Teamkarten auf mobilen Geräten */
@media (max-width: 768px) {
.teamcard-grid {
justify-content: space-evenly;
}
.teamcard {
width: 100%;
max-width: 320px;
}
}

View File

@@ -1,237 +0,0 @@
jQuery(document).ready(function($) {
// Medienbibliothek für neues Teammitglied
var newTeamcardFrame;
$('#new-teamcard-bild-button').on('click', function(e) {
e.preventDefault();
if (newTeamcardFrame) {
newTeamcardFrame.open();
return;
}
newTeamcardFrame = wp.media({
title: 'Bild auswählen',
button: { text: 'Verwenden' },
multiple: false
});
newTeamcardFrame.on('select', function() {
var attachment = newTeamcardFrame.state().get('selection').first().toJSON();
$('#new-teamcard-bild-id').val(attachment.id);
$('#new-teamcard-bild-vorschau').attr('src', attachment.url).show();
});
newTeamcardFrame.open();
});
// Medienbibliothek für vorhandene Teammitglieder
var teamcardFrame;
$(document).on('click', '.teamcard-bild-button', function(e) {
e.preventDefault();
var button = $(this);
var teamcardId = button.data('id');
if (teamcardFrame) {
teamcardFrame.open();
return;
}
teamcardFrame = wp.media({
title: 'Bild auswählen',
button: { text: 'Verwenden' },
multiple: false
});
teamcardFrame.on('select', function() {
var attachment = teamcardFrame.state().get('selection').first().toJSON();
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: {
action: 'update_teamcard_image',
id: teamcardId,
bild_id: attachment.id,
nonce: teamcard_data.nonce
},
success: function(response) {
if (response.success) {
var container = button.siblings('.image-preview-container');
container.html('<img src="' + response.data.bild_url + '" class="teamcard-bild-vorschau">');
showMessage('Bild erfolgreich aktualisiert', 'success');
}
}
});
});
teamcardFrame.open();
});
// Neues Teammitglied hinzufügen
$('#add-teamcard-button').on('click', function() {
var name = $('#new-teamcard-name').val();
var funktion = $('#new-teamcard-funktion').val();
var zustaendigkeit = $('#new-teamcard-zustaendigkeit').val();
var bildId = $('#new-teamcard-bild-id').val();
if (!name) {
showMessage('Bitte gib einen Namen ein', 'error');
return;
}
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: {
action: 'add_teamcard',
name: name,
funktion: funktion,
zustaendigkeit: zustaendigkeit,
bild_id: bildId,
nonce: teamcard_data.nonce
},
success: function(response) {
if (response.success) {
var bildHtml = '';
if (response.data.bild_url) {
bildHtml = '<img src="' + response.data.bild_url + '" class="teamcard-bild-vorschau">';
}
var newRow = '<tr data-id="' + response.data.id + '" class="teamcard-item">' +
'<td class="teamcard-bild">' +
'<div class="image-preview-container">' + bildHtml + '</div>' +
'<button type="button" class="button teamcard-bild-button" data-id="' + response.data.id + '">Bild ändern</button>' +
'</td>' +
'<td><div class="editable" data-field="title" data-id="' + response.data.id + '">' + name + '</div></td>' +
'<td><div class="editable" data-field="funktion" data-id="' + response.data.id + '">' + funktion + '</div></td>' +
'<td><div class="editable" data-field="zustaendigkeit" data-id="' + response.data.id + '">' + zustaendigkeit + '</div></td>' +
'<td><button type="button" class="button button-small teamcard-delete" data-id="' + response.data.id + '">Löschen</button></td>' +
'</tr>';
$('#teamcard-list').append(newRow);
// Formular zurücksetzen
$('#new-teamcard-name').val('');
$('#new-teamcard-funktion').val('');
$('#new-teamcard-zustaendigkeit').val('');
$('#new-teamcard-bild-id').val('');
$('#new-teamcard-bild-vorschau').attr('src', '').hide();
showMessage('Teammitglied erfolgreich hinzugefügt', 'success');
} else {
showMessage(response.data.message, 'error');
}
}
});
});
// Inline-Bearbeitung
$(document).on('click', '.editable', function() {
var element = $(this);
var currentText = element.text();
element.html('<input type="text" class="inline-edit" value="' + currentText + '">');
element.find('input').focus().select();
});
$(document).on('blur', '.inline-edit', function() {
var input = $(this);
var element = input.parent();
var newValue = input.val();
var field = element.data('field');
var id = element.data('id');
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: {
action: 'update_teamcard',
id: id,
field: field,
value: newValue,
nonce: teamcard_data.nonce
},
success: function(response) {
if (response.success) {
element.text(newValue);
showMessage('Erfolgreich aktualisiert', 'success');
}
}
});
});
$(document).on('keypress', '.inline-edit', function(e) {
if (e.which === 13) {
$(this).blur();
}
});
// Teammitglied löschen
$(document).on('click', '.teamcard-delete', function() {
if (!confirm('Möchtest du dieses Teammitglied wirklich löschen?')) {
return;
}
var button = $(this);
var id = button.data('id');
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: {
action: 'delete_teamcard',
id: id,
nonce: teamcard_data.nonce
},
success: function(response) {
if (response.success) {
button.closest('tr').remove();
showMessage('Teammitglied erfolgreich gelöscht', 'success');
} else {
showMessage(response.data.message, 'error');
}
}
});
});
// Drag & Drop Sortierung
$('#teamcard-list').sortable({
handle: 'td:first',
placeholder: 'ui-state-highlight',
update: function(event, ui) {
var order = [];
$('.teamcard-item').each(function() {
order.push($(this).data('id'));
});
$.ajax({
url: teamcard_data.ajax_url,
type: 'POST',
data: {
action: 'update_teamcard_order',
order: order,
nonce: teamcard_data.nonce
},
success: function(response) {
if (response.success) {
showMessage('Reihenfolge erfolgreich aktualisiert', 'success');
}
}
});
}
});
// Hilfsfunktion für Nachrichten
function showMessage(message, type) {
var messageElement = $('#teamcard-message');
messageElement.removeClass('notice-success notice-error').addClass('notice-' + type);
messageElement.html('<p>' + message + '</p>').show();
setTimeout(function() {
messageElement.fadeOut();
}, 3000);
}
});

View File

@@ -0,0 +1,18 @@
<?php
/**
* @var array $meta Enthält alle Metadaten des Teammitglieds
* @var string $column_style Der Inline-Style für die Spaltenbreite
*/
?>
<div class="teamcard teamcard-compact" <?php echo $column_style; ?>>
<?php if ($meta['bild_url']): ?>
<div class="teamcard-image">
<img src="<?php echo esc_url($meta['bild_url']); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
</div>
<?php endif; ?>
<div class="teamcard-content">
<h3 class="teamcard-name"><?php the_title(); ?></h3>
<p class="teamcard-function"><?php echo esc_html($meta['funktion']); ?></p>
</div>
</div>

View File

@@ -0,0 +1,19 @@
<?php
/**
* @var array $meta Enthält alle Metadaten des Teammitglieds
* @var string $column_style Der Inline-Style für die Spaltenbreite
*/
?>
<div class="teamcard teamcard-featured" <?php echo $column_style; ?>>
<?php if ($meta['bild_url']): ?>
<div class="teamcard-image">
<img src="<?php echo esc_url($meta['bild_url']); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
</div>
<?php endif; ?>
<div class="teamcard-content">
<h3 class="teamcard-name"><?php the_title(); ?></h3>
<p class="teamcard-function"><?php echo esc_html($meta['funktion']); ?></p>
<p class="teamcard-responsibility"><?php echo esc_html($meta['zustaendigkeit']); ?></p>
</div>
</div>

35
templates/card-flip.php Normal file
View File

@@ -0,0 +1,35 @@
<?php
/**
* @var array $meta Enthält alle Metadaten des Teammitglieds
* @var string $column_style Der Inline-Style für die Spaltenbreite
*/
?>
<div class="teamcard-flip-container" <?php echo $column_style; ?>>
<div class="teamcard-flipper">
<!-- Vorderseite der Karte -->
<div class="teamcard teamcard-flip teamcard-front">
<?php if ($meta['bild_url']): ?>
<div class="teamcard-image">
<img src="<?php echo esc_url($meta['bild_url']); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
</div>
<?php endif; ?>
<div class="teamcard-content">
<h3 class="teamcard-name"><?php the_title(); ?></h3>
<p class="teamcard-function"><?php echo esc_html($meta['funktion']); ?></p>
</div>
</div>
<!-- Rückseite der Karte -->
<div class="teamcard teamcard-flip teamcard-back">
<div class="teamcard-content">
<h3 class="teamcard-name"><?php the_title(); ?></h3>
<div class="teamcard-responsibility">
<?php
// nl2br() wandelt Zeilenumbrüche in <br>-Tags um
echo wp_kses_post(nl2br($meta['zustaendigkeit']));
?>
</div>
</div>
</div>
</div>
</div>

View File

@@ -0,0 +1,17 @@
<?php
/**
* @var array $meta Enthält alle Metadaten des Teammitglieds
* @var string $column_style Der Inline-Style für die Spaltenbreite
*/
?>
<div class="teamcard teamcard-profile" <?php echo $column_style; ?>>
<div class="teamcard-profile-header" <?php echo $meta['bild_url'] ? 'style="background-image: url(' . esc_url($meta['bild_url']) . ');"' : ''; ?>>
<div class="teamcard-profile-content">
<h3 class="teamcard-name"><?php the_title(); ?></h3>
<p class="teamcard-function"><?php echo esc_html($meta['funktion']); ?></p>
</div>
</div>
<div class="teamcard-profile-body">
<p class="teamcard-responsibility"><?php echo esc_html($meta['zustaendigkeit']); ?></p>
</div>
</div>

View File

@@ -0,0 +1,19 @@
<?php
/**
* @var array $meta Enthält alle Metadaten des Teammitglieds
* @var string $column_style Der Inline-Style für die Spaltenbreite
*/
?>
<div class="teamcard teamcard-standard" <?php echo $column_style; ?>>
<?php if ($meta['bild_url']): ?>
<div class="teamcard-image">
<img src="<?php echo esc_url($meta['bild_url']); ?>" alt="<?php echo esc_attr(get_the_title()); ?>">
</div>
<?php endif; ?>
<div class="teamcard-content">
<h3 class="teamcard-name"><?php the_title(); ?></h3>
<p class="teamcard-function"><?php echo esc_html($meta['funktion']); ?></p>
<p class="teamcard-responsibility"><?php echo esc_html($meta['zustaendigkeit']); ?></p>
</div>
</div>

48
uninstall.php Normal file
View File

@@ -0,0 +1,48 @@
<?php
// Sicherstellen, dass die Datei nur bei Plugin-Deinstallation direkt von WordPress aufgerufen wird
if (!defined('WP_UNINSTALL_PLUGIN')) {
exit;
}
// Funktion zum Löschen aller Plugin-Daten
function delete_teamcard_data() {
// Alle Teamcard-Posts abrufen
$teamcards = get_posts([
'post_type' => 'teamcard',
'posts_per_page' => -1,
'post_status' => 'any',
]);
// Jeden Post und zugehörige Bilder löschen
foreach ($teamcards as $teamcard) {
// Bild aus der Mediathek löschen, falls vorhanden
$bild_id = get_post_meta($teamcard->ID, '_teamcard_bild_id', true);
if ($bild_id) {
wp_delete_attachment($bild_id, true);
}
// Post und Metadaten löschen
wp_delete_post($teamcard->ID, true);
}
// Taxonomie 'teamcard_kategorie' und ihre Begriffe löschen
$terms = get_terms([
'taxonomy' => 'teamcard_kategorie',
'hide_empty' => false,
]);
if (!is_wp_error($terms)) {
foreach ($terms as $term) {
wp_delete_term($term->term_id, 'teamcard_kategorie');
}
}
// Alle Metadaten löschen, die mit dem Plugin verknüpft sind
global $wpdb;
$wpdb->query("DELETE FROM $wpdb->postmeta WHERE meta_key LIKE '_teamcard_%'");
// Rewrite Rules zurücksetzen
flush_rewrite_rules();
}
// Funktion ausführen
delete_teamcard_data();

View File

@@ -1,359 +0,0 @@
<?php
/**
* Plugin Name: WP Multi Team-Card
* Plugin URI: https://git.viper.ipv64.net/M_Viper/wp-multi
* Description: Erstellt Teamkarten mit Name, Funktion, Zuständigkeit, Bild und Kategorie. Ausgabe per Shortcode [teamcards].
* Version: 1.0
* Author: M_Viper
* Author URI: https://m-viper.de
* Requires at least: 6.7.2
* Tested up to: 6.7.2
* PHP Version: 7.2
* License: GPL2
* License URI: https://www.gnu.org/licenses/gpl-2.0.html
* Text Domain: wp-multi-team-card
* Tags: team, card, shortcodes, team-management, customizable, responsive
* Support: [Microsoft Teams Support](https://teams.live.com/l/community/FEAzokphpZTJ2u6OgI)
* Support: [Telegram Support](https://t.me/M_Viper04)
*/
if (!defined('ABSPATH')) exit;
// Check if the PHP version is sufficient
if (version_compare(PHP_VERSION, '7.2', '<')) {
die('This plugin requires PHP 7.2 or higher.');
}
// CPT registrieren
function teamcard_register_post_type() {
register_post_type('teamcard', [
'labels' => [
'name' => 'Teammitglieder',
'singular_name' => 'Teammitglied',
'add_new' => 'Neues Teammitglied',
'add_new_item' => 'Teammitglied hinzufügen',
'edit_item' => 'Teammitglied bearbeiten',
],
'public' => true,
'show_ui' => false, // Standard-UI ausblenden
'show_in_menu' => false, // Nicht im Hauptmenü anzeigen
'menu_icon' => 'dashicons-groups',
'supports' => ['title'],
'has_archive' => false,
'show_in_admin_bar' => false,
]);
register_taxonomy('teamcard_kategorie', 'teamcard', [
'labels' => [
'name' => 'Kategorien',
'singular_name' => 'Kategorie',
],
'hierarchical' => true,
'public' => true,
'show_admin_column' => true,
]);
}
add_action('init', 'teamcard_register_post_type');
// Admin-Menü hinzufügen
function teamcard_add_admin_menu() {
add_menu_page(
'Teammitglieder verwalten',
'Teamkarten',
'manage_options',
'teamcard_management',
'teamcard_admin_page',
'dashicons-groups',
30
);
}
add_action('admin_menu', 'teamcard_add_admin_menu');
// Die Hauptverwaltungsseite
function teamcard_admin_page() {
?>
<div class="wrap teamcard-admin">
<h1>Teammitglieder verwalten</h1>
<!-- Formular zum Hinzufügen neuer Teammitglieder -->
<div class="teamcard-add-new-form">
<h2>Neues Teammitglied hinzufügen</h2>
<div class="form-fields">
<div class="form-field">
<label for="new-teamcard-name">Name:</label>
<input type="text" id="new-teamcard-name" placeholder="Name eingeben">
</div>
<div class="form-field">
<label for="new-teamcard-funktion">Funktion:</label>
<input type="text" id="new-teamcard-funktion" placeholder="Funktion eingeben">
</div>
<div class="form-field">
<label for="new-teamcard-zustaendigkeit">Zuständigkeit:</label>
<input type="text" id="new-teamcard-zustaendigkeit" placeholder="Zuständigkeit eingeben">
</div>
<div class="form-field">
<label>Bild:</label>
<div class="image-preview-container">
<img id="new-teamcard-bild-vorschau" src="" style="display:none; max-width:100px;">
</div>
<input type="hidden" id="new-teamcard-bild-id" value="">
<button type="button" class="button" id="new-teamcard-bild-button">Bild auswählen</button>
</div>
<div class="form-field">
<button type="button" id="add-teamcard-button" class="button button-primary">Teammitglied hinzufügen</button>
</div>
</div>
</div>
<h2>Vorhandene Teammitglieder</h2>
<p>Ziehe die Zeilen, um die Reihenfolge zu ändern. Klicke auf die Felder, um sie zu bearbeiten.</p>
<!-- Liste der vorhandenen Teammitglieder -->
<table class="wp-list-table widefat fixed striped">
<thead>
<tr>
<th width="10%">Bild</th>
<th width="20%">Name</th>
<th width="20%">Funktion</th>
<th width="30%">Zuständigkeit</th>
<th width="20%">Aktionen</th>
</tr>
</thead>
<tbody id="teamcard-list">
<?php
$teamcards = get_posts([
'post_type' => 'teamcard',
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC'
]);
foreach ($teamcards as $teamcard) {
$funktion = get_post_meta($teamcard->ID, '_teamcard_funktion', true);
$zustaendigkeit = get_post_meta($teamcard->ID, '_teamcard_zustaendigkeit', true);
$bild_id = get_post_meta($teamcard->ID, '_teamcard_bild_id', true);
$bild_url = $bild_id ? wp_get_attachment_image_url($bild_id, 'thumbnail') : '';
?>
<tr data-id="<?php echo $teamcard->ID; ?>" class="teamcard-item">
<td class="teamcard-bild">
<div class="image-preview-container">
<?php if ($bild_url): ?>
<img src="<?php echo esc_url($bild_url); ?>" class="teamcard-bild-vorschau">
<?php endif; ?>
</div>
<button type="button" class="button teamcard-bild-button" data-id="<?php echo $teamcard->ID; ?>">Bild ändern</button>
</td>
<td>
<div class="editable" data-field="title" data-id="<?php echo $teamcard->ID; ?>"><?php echo esc_html($teamcard->post_title); ?></div>
</td>
<td>
<div class="editable" data-field="funktion" data-id="<?php echo $teamcard->ID; ?>"><?php echo esc_html($funktion); ?></div>
</td>
<td>
<div class="editable" data-field="zustaendigkeit" data-id="<?php echo $teamcard->ID; ?>"><?php echo esc_html($zustaendigkeit); ?></div>
</td>
<td>
<button type="button" class="button button-small teamcard-delete" data-id="<?php echo $teamcard->ID; ?>">Löschen</button>
</td>
</tr>
<?php
}
?>
</tbody>
</table>
<div id="teamcard-message" class="notice" style="display:none;"></div>
</div>
<?php
}
// AJAX-Handler zum Speichern der Teammitglieder
function teamcard_ajax_handlers() {
// Neues Teammitglied hinzufügen
add_action('wp_ajax_add_teamcard', function() {
$name = sanitize_text_field($_POST['name']);
$funktion = sanitize_text_field($_POST['funktion']);
$zustaendigkeit = sanitize_text_field($_POST['zustaendigkeit']);
$bild_id = intval($_POST['bild_id']);
// Höchste menu_order finden
$max_order = 0;
$posts = get_posts([
'post_type' => 'teamcard',
'posts_per_page' => 1,
'orderby' => 'menu_order',
'order' => 'DESC'
]);
if (!empty($posts)) {
$max_order = $posts[0]->menu_order;
}
// Neuen Post erstellen
$post_id = wp_insert_post([
'post_type' => 'teamcard',
'post_title' => $name,
'post_status' => 'publish',
'menu_order' => $max_order + 1
]);
if ($post_id) {
update_post_meta($post_id, '_teamcard_funktion', $funktion);
update_post_meta($post_id, '_teamcard_zustaendigkeit', $zustaendigkeit);
if ($bild_id > 0) {
update_post_meta($post_id, '_teamcard_bild_id', $bild_id);
}
$bild_url = $bild_id ? wp_get_attachment_image_url($bild_id, 'thumbnail') : '';
wp_send_json_success([
'id' => $post_id,
'bild_url' => $bild_url
]);
} else {
wp_send_json_error(['message' => 'Fehler beim Erstellen des Teammitglieds']);
}
});
// Teammitglied aktualisieren
add_action('wp_ajax_update_teamcard', function() {
$post_id = intval($_POST['id']);
$field = sanitize_text_field($_POST['field']);
$value = sanitize_text_field($_POST['value']);
if ($field === 'title') {
wp_update_post([
'ID' => $post_id,
'post_title' => $value
]);
} else {
update_post_meta($post_id, '_teamcard_' . $field, $value);
}
wp_send_json_success();
});
// Teammitglied löschen
add_action('wp_ajax_delete_teamcard', function() {
$post_id = intval($_POST['id']);
if (wp_delete_post($post_id, true)) {
wp_send_json_success();
} else {
wp_send_json_error(['message' => 'Fehler beim Löschen des Teammitglieds']);
}
});
// Bild aktualisieren
add_action('wp_ajax_update_teamcard_image', function() {
$post_id = intval($_POST['id']);
$bild_id = intval($_POST['bild_id']);
update_post_meta($post_id, '_teamcard_bild_id', $bild_id);
$bild_url = wp_get_attachment_image_url($bild_id, 'thumbnail');
wp_send_json_success(['bild_url' => $bild_url]);
});
// Reihenfolge aktualisieren
add_action('wp_ajax_update_teamcard_order', function() {
$order = $_POST['order'];
foreach ($order as $position => $post_id) {
wp_update_post([
'ID' => intval($post_id),
'menu_order' => $position
]);
}
wp_send_json_success();
});
}
add_action('init', 'teamcard_ajax_handlers');
// Admin-Skripte und Styles laden
function teamcard_admin_scripts($hook) {
if ($hook !== 'toplevel_page_teamcard_management') return;
wp_enqueue_media();
wp_enqueue_script('jquery-ui-sortable');
wp_enqueue_style(
'teamcard-admin-style',
plugin_dir_url(__FILE__) . 'teamcard-admin.css',
[],
'1.0'
);
wp_enqueue_script(
'teamcard-admin-script',
plugin_dir_url(__FILE__) . 'teamcard-admin.js',
['jquery', 'jquery-ui-sortable'],
'1.0',
true
);
wp_localize_script('teamcard-admin-script', 'teamcard_data', [
'ajax_url' => admin_url('admin-ajax.php'),
'nonce' => wp_create_nonce('teamcard_nonce')
]);
}
add_action('admin_enqueue_scripts', 'teamcard_admin_scripts');
// Shortcode für die Frontend-Anzeige
function teamcard_shortcode($atts) {
$atts = shortcode_atts([
'kategorie' => '',
], $atts);
$args = [
'post_type' => 'teamcard',
'posts_per_page' => -1,
'orderby' => 'menu_order',
'order' => 'ASC',
];
if ($atts['kategorie']) {
$args['tax_query'] = [[
'taxonomy' => 'teamcard_kategorie',
'field' => 'slug',
'terms' => $atts['kategorie'],
]];
}
$query = new WP_Query($args);
if (!$query->have_posts()) return '<p>Keine Teammitglieder gefunden.</p>';
ob_start();
echo '<div class="teamcard-grid" style="display: flex; flex-wrap: wrap; gap: 20px;">';
while ($query->have_posts()) {
$query->the_post();
$funktion = get_post_meta(get_the_ID(), '_teamcard_funktion', true);
$zustaendigkeit = get_post_meta(get_the_ID(), '_teamcard_zustaendigkeit', true);
$bild_id = get_post_meta(get_the_ID(), '_teamcard_bild_id', true);
$bild_url = $bild_id ? wp_get_attachment_url($bild_id) : '';
echo '<div class="teamcard" style="display: flex; flex-wrap: wrap; border:1px solid #ccc; border-radius:10px; padding:15px; width:48%; align-items: center;">';
// Bild links
if ($bild_url) {
echo '<div style="flex: 1; text-align: center; display: flex; justify-content: center; align-items: center; margin-right: 10px;">';
echo '<img src="' . esc_url($bild_url) . '" style="max-width:100px; border-radius:50%; margin-bottom:0;">';
echo '</div>';
}
// Text rechts
echo '<div style="flex: 2; padding-left: 10px;">';
echo '<h3>' . get_the_title() . '</h3>';
echo '<p><strong>' . esc_html($funktion) . '</strong></p>';
echo '<p>' . esc_html($zustaendigkeit) . '</p>';
echo '</div>';
echo '</div>'; // Schließt die Teamcard-Div
}
echo '</div>'; // Schließt die Teamcard-Grid-Div
wp_reset_postdata();
return ob_get_clean();
}
add_shortcode('teamcards', 'teamcard_shortcode');