summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRoger Rehnelt <noreply@maennchen1.de>2026-10-04 15:44:05 +0200
committerRoger Rehnelt <noreply@maennchen1.de>2026-10-04 15:44:05 +0200
commit9f73ac8d79ae3050e0162f3c48e78518df21a09b (patch)
tree3c138747735226cea115c05fa209634bc49bad9b
parentb592f99d16f124a889e50c2727a8f5a41d9b473f (diff)
Produktbewertungen: Modul mit persönlichem Link, Formular und Freigabe
Käufer bewerten je Bestellposition über einen Link von der Abschlussseite; das Backend gibt frei, lehnt ab und antwortet. Noch ohne Anzeige im Shop. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
-rw-r--r--views/mods/mod_reviews/admin_index.phtml81
-rw-r--r--views/mods/mod_reviews/order_done.phtml15
-rw-r--r--views/mods/mod_reviews/page.phtml75
-rw-r--r--views/mods/mod_reviews/settings_edit.phtml20
4 files changed, 191 insertions, 0 deletions
diff --git a/views/mods/mod_reviews/admin_index.phtml b/views/mods/mod_reviews/admin_index.phtml
new file mode 100644
index 0000000..7ee6661
--- /dev/null
+++ b/views/mods/mod_reviews/admin_index.phtml
@@ -0,0 +1,81 @@
+<?php
+ /**
+ * Backend: Bewertungen prüfen, freigeben, ablehnen, beantworten
+ * @version 4.6.1
+ */
+ defined('ABSPATH') || exit;
+
+ $admin = $this->view['wpsg_mod_reviews']['admin'];
+ $arTabs = [
+ wpsg_review::STATUS_PENDING => __('Zu prüfen', 'wpsg'),
+ wpsg_review::STATUS_APPROVED => __('Veröffentlicht', 'wpsg'),
+ wpsg_review::STATUS_REJECTED => __('Abgelehnt', 'wpsg')
+ ];
+ $base = admin_url('admin.php?page=wpsg-Reviews');
+?>
+<div class="wrap wpsg-reviews-admin">
+
+ <h1><?php echo esc_html__('Bewertungen', 'wpsg'); ?></h1>
+
+ <?php echo $this->writeBackendMessage(); ?>
+
+ <nav class="nav-tab-wrapper" aria-label="<?php echo esc_attr__('Bewertungen nach Status', 'wpsg'); ?>">
+ <?php foreach ($arTabs as $status => $label) { ?>
+ <a href="<?php echo esc_url(add_query_arg('status', $status, $base)); ?>" class="nav-tab<?php echo (($admin['status'] === $status)?' nav-tab-active':''); ?>"<?php if ($admin['status'] === $status) { ?> aria-current="page"<?php } ?>><?php echo esc_html($label); ?> (<?php echo intval($admin['count'][$status]??0); ?>)</a>
+ <?php } ?>
+ </nav>
+
+ <?php if (sizeof($admin['reviews']) === 0) { ?>
+
+ <p><?php echo esc_html__('Keine Bewertungen.', 'wpsg'); ?></p>
+
+ <?php } else { ?>
+
+ <table class="widefat striped">
+ <thead>
+ <tr>
+ <th scope="col"><?php echo esc_html__('Produkt', 'wpsg'); ?></th>
+ <th scope="col"><?php echo esc_html__('Sterne', 'wpsg'); ?></th>
+ <th scope="col"><?php echo esc_html__('Bewertung', 'wpsg'); ?></th>
+ <th scope="col"><?php echo esc_html__('Name', 'wpsg'); ?></th>
+ <th scope="col"><?php echo esc_html__('Abgegeben', 'wpsg'); ?></th>
+ <th scope="col"><?php echo esc_html__('Aktion', 'wpsg'); ?></th>
+ </tr>
+ </thead>
+ <tbody>
+ <?php foreach ($admin['reviews'] as $review) { $review_id = intval($review['id']); ?>
+ <tr>
+ <td><?php echo esc_html($review['product']); ?><br />
+ <small><?php echo ((intval($review['verified']) === 1)?esc_html__('Verifizierter Kauf', 'wpsg'):esc_html__('Ohne Kaufnachweis (übernommen)', 'wpsg')); ?></small></td>
+ <td><span aria-hidden="true"><?php echo str_repeat('★', intval($review['stars'])).str_repeat('☆', 5 - intval($review['stars'])); ?></span>
+ <span class="screen-reader-text"><?php echo esc_html(wpsg_translate(__('#1# von 5 Sternen', 'wpsg'), intval($review['stars']))); ?></span></td>
+ <td><?php if ((string)$review['title'] !== '') { ?><strong><?php echo esc_html($review['title']); ?></strong><br /><?php } ?>
+ <?php echo nl2br(esc_html((string)$review['text'])); ?>
+ <?php if ((string)$review['reply'] !== '') { ?><p><em><?php echo esc_html__('Ihre Antwort:', 'wpsg'); ?></em> <?php echo nl2br(esc_html((string)$review['reply'])); ?></p><?php } ?></td>
+ <td><?php echo esc_html($review['display_name']); ?></td>
+ <td><?php echo esc_html((string)$review['submitted']); ?></td>
+ <td>
+ <?php foreach (['approve' => __('Freigeben', 'wpsg'), 'reject' => __('Ablehnen', 'wpsg'), 'delete' => __('Löschen', 'wpsg')] as $action => $label) { ?>
+ <?php if (($action === 'approve' && $review['status'] === wpsg_review::STATUS_APPROVED) || ($action === 'reject' && $review['status'] === wpsg_review::STATUS_REJECTED)) continue; ?>
+ <form method="post" class="wpsg-reviews-admin__action">
+ <?php wp_nonce_field('wpsg_reviews_moderate'); ?>
+ <input type="hidden" name="review_id" value="<?php echo $review_id; ?>" />
+ <button type="submit" name="wpsg_review_action" value="<?php echo esc_attr($action); ?>" class="button<?php echo (($action === 'approve')?' button-primary':''); ?>"<?php if ($action === 'delete') { ?> onclick="return confirm('<?php echo esc_js(__('Bewertung endgültig löschen?', 'wpsg')); ?>');"<?php } ?>><?php echo esc_html($label); ?></button>
+ </form>
+ <?php } ?>
+ <form method="post" class="wpsg-reviews-admin__reply">
+ <?php wp_nonce_field('wpsg_reviews_moderate'); ?>
+ <input type="hidden" name="review_id" value="<?php echo $review_id; ?>" />
+ <label for="wpsg_review_reply_<?php echo $review_id; ?>"><?php echo esc_html__('Öffentliche Antwort', 'wpsg'); ?></label>
+ <textarea id="wpsg_review_reply_<?php echo $review_id; ?>" name="reply" rows="2"><?php echo esc_textarea((string)$review['reply']); ?></textarea>
+ <button type="submit" name="wpsg_review_action" value="reply" class="button"><?php echo esc_html__('Antwort speichern', 'wpsg'); ?></button>
+ </form>
+ </td>
+ </tr>
+ <?php } ?>
+ </tbody>
+ </table>
+
+ <?php } ?>
+
+</div>
diff --git a/views/mods/mod_reviews/order_done.phtml b/views/mods/mod_reviews/order_done.phtml
new file mode 100644
index 0000000..00af294
--- /dev/null
+++ b/views/mods/mod_reviews/order_done.phtml
@@ -0,0 +1,15 @@
+<?php
+ /**
+ * Abschlussseite: persönliche Links zum Bewerten der bestellten Produkte
+ * @version 4.6.1
+ */
+ defined('ABSPATH') || exit;
+?>
+<div class="wpsg-reviews-invite">
+ <p><?php echo esc_html__('Sobald Ihre Bestellung bei Ihnen ist, können Sie die Produkte über diese persönlichen Links bewerten. Bewahren Sie sie auf, etwa als Lesezeichen:', 'wpsg'); ?></p>
+ <ul>
+ <?php foreach ((array)$this->view['wpsg_mod_reviews']['done'] as $review) { ?>
+ <li><a href="<?php echo esc_url($review['url']); ?>"><?php echo esc_html(wpsg_translate(__('#1# bewerten', 'wpsg'), $review['name'])); ?></a></li>
+ <?php } ?>
+ </ul>
+</div>
diff --git a/views/mods/mod_reviews/page.phtml b/views/mods/mod_reviews/page.phtml
new file mode 100644
index 0000000..788c4bd
--- /dev/null
+++ b/views/mods/mod_reviews/page.phtml
@@ -0,0 +1,75 @@
+<?php
+ /**
+ * Seite „Bewertung abgeben“: Formular zum persönlichen Link
+ * @version 4.6.1
+ */
+ defined('ABSPATH') || exit;
+
+ $page = $this->view['wpsg_mod_reviews']['page'];
+?>
+<div class="wpsg wpsg-review-page">
+
+ <?php if ($page['thanks']) { ?>
+
+ <p class="wpsg-review-page__message" role="status"><?php echo esc_html((($page['status']??'') === wpsg_review::STATUS_APPROVED)?__('Vielen Dank für Ihre Bewertung. Sie ist jetzt veröffentlicht.', 'wpsg'):__('Vielen Dank für Ihre Bewertung. Sie erscheint, sobald wir sie freigegeben haben.', 'wpsg')); ?></p>
+
+ <?php } else if ($page['state'] === 'invalid') { ?>
+
+ <p class="wpsg-review-page__message"><?php echo esc_html__('Dieser Link ist ungültig. Bitte verwenden Sie den vollständigen Link aus Ihrer Bestellung.', 'wpsg'); ?></p>
+
+ <?php } else if ($page['state'] === 'done') { ?>
+
+ <p class="wpsg-review-page__message"><?php echo esc_html(wpsg_translate(__('Sie haben „#1#“ bereits bewertet. Vielen Dank!', 'wpsg'), $page['product'])); ?></p>
+
+ <?php } else if ($page['state'] === 'early') { ?>
+
+ <p class="wpsg-review-page__message"><?php echo esc_html(wpsg_translate(__('Sie können „#1#“ bewerten, sobald Ihre Bestellung bei Ihnen ist. Der Link bleibt gültig.', 'wpsg'), $page['product'])); ?></p>
+
+ <?php } else { $errors = $page['errors']; $data = $page['data']; ?>
+
+ <h2 class="wpsg-review-page__title"><?php echo esc_html(wpsg_translate(__('„#1#“ bewerten', 'wpsg'), $page['product'])); ?></h2>
+
+ <?php if (sizeof($errors) > 0) { ?>
+ <div class="wpsg-review-page__errors" role="alert">
+ <ul><?php foreach ($errors as $error) { ?><li><?php echo esc_html($error); ?></li><?php } ?></ul>
+ </div>
+ <?php } ?>
+
+ <form method="post" action="<?php echo esc_url(add_query_arg(wpsg_mod_reviews::PARAM, $page['token'], get_permalink())); ?>" class="wpsg-review-form" novalidate>
+
+ <?php wp_nonce_field('wpsg_review_submit_'.$page['review_id']); ?>
+ <input type="hidden" name="<?php echo esc_attr(wpsg_mod_reviews::PARAM); ?>" value="<?php echo esc_attr($page['token']); ?>" />
+
+ <fieldset class="wpsg-review-form__stars"<?php if (isset($errors['stars'])) { ?> aria-invalid="true"<?php } ?>>
+ <legend><?php echo esc_html__('Ihre Bewertung', 'wpsg'); ?> <span class="wpsg_required">*</span></legend>
+ <?php for ($i = 5; $i >= 1; $i--) { ?>
+ <label><input type="radio" name="wpsg_review[stars]" value="<?php echo $i; ?>" <?php checked(intval($data['stars']), $i); ?> required /> <span aria-hidden="true"><?php echo str_repeat('★', $i).str_repeat('☆', 5 - $i); ?></span> <span class="wpsg-review-form__stars-text"><?php echo esc_html(wpsg_translate(_n('#1# Stern', '#1# Sterne', $i, 'wpsg'), $i)); ?></span></label>
+ <?php } ?>
+ </fieldset>
+
+ <p>
+ <label for="wpsg_review_title"><?php echo esc_html__('Überschrift', 'wpsg'); ?></label>
+ <input type="text" id="wpsg_review_title" name="wpsg_review[title]" maxlength="<?php echo intval(wpsg_review::MAX_TITLE); ?>" value="<?php echo esc_attr((string)$data['title']); ?>" />
+ </p>
+
+ <p>
+ <label for="wpsg_review_text"><?php echo esc_html__('Ihre Erfahrung', 'wpsg'); ?></label>
+ <textarea id="wpsg_review_text" name="wpsg_review[text]" rows="6" maxlength="<?php echo intval(wpsg_review::MAX_TEXT); ?>"><?php echo esc_textarea((string)$data['text']); ?></textarea>
+ </p>
+
+ <p>
+ <label for="wpsg_review_name"><?php echo esc_html__('Anzeigename', 'wpsg'); ?> <span class="wpsg_required">*</span></label>
+ <input type="text" id="wpsg_review_name" name="wpsg_review[display_name]" maxlength="<?php echo intval(wpsg_review::MAX_NAME); ?>" autocomplete="nickname" value="<?php echo esc_attr((string)$data['display_name']); ?>" required<?php if (isset($errors['display_name'])) { ?> aria-invalid="true"<?php } ?> />
+ </p>
+
+ <p>
+ <label><input type="checkbox" name="wpsg_review[consent]" value="1" <?php checked((string)$data['consent'], '1'); ?> required<?php if (isset($errors['consent'])) { ?> aria-invalid="true"<?php } ?> /> <?php echo esc_html__('Ich bin einverstanden, dass meine Bewertung mit dem Anzeigenamen auf dieser Website veröffentlicht wird. Ich kann das jederzeit widerrufen.', 'wpsg'); ?> <span class="wpsg_required">*</span></label>
+ </p>
+
+ <p><button type="submit" name="wpsg_review_submit" value="1" class="wpsg_button<?php echo wpsg_button_class(); ?>"><?php echo esc_html__('Bewertung absenden', 'wpsg'); ?></button></p>
+
+ </form>
+
+ <?php } ?>
+
+</div>
diff --git a/views/mods/mod_reviews/settings_edit.phtml b/views/mods/mod_reviews/settings_edit.phtml
new file mode 100644
index 0000000..ddfd423
--- /dev/null
+++ b/views/mods/mod_reviews/settings_edit.phtml
@@ -0,0 +1,20 @@
+<?php
+ /**
+ * Einstellungen des Moduls Produktbewertungen
+ * @version 4.6.1
+ */
+ defined('ABSPATH') || exit;
+
+ $arStatus = [];
+ foreach ([wpsg_ShopController::STATUS_ZAHLUNGAKZEPTIERT, wpsg_ShopController::STATUS_RECHNUNGGESCHRIEBEN, wpsg_ShopController::STATUS_WAREVERSENDET, wpsg_ShopController::STATUS_ZUGESTELLT] as $status) {
+
+ if (isset($this->view['wpsg_mod_reviews']['status'][$status])) $arStatus[$status] = $this->view['wpsg_mod_reviews']['status'][$status];
+
+ }
+?>
+<?php echo wpsg_drawForm_Select('wpsg_page_review', __('Seite „Bewertung abgeben“', 'wpsg'), $this->view['wpsg_mod_reviews']['pages'], $this->get_option('wpsg_page_review')); ?>
+<?php echo wpsg_drawForm_Select('wpsg_mod_reviews_from_status', __('Bewerten möglich ab Status', 'wpsg'), $arStatus, $this->get_option('wpsg_mod_reviews_from_status')); ?>
+<?php echo wpsg_drawForm_Select('wpsg_mod_reviews_approval', __('Neue Bewertungen', 'wpsg'), [
+ wpsg_mod_reviews::APPROVAL_MANUAL => __('Vor der Veröffentlichung prüfen', 'wpsg'),
+ wpsg_mod_reviews::APPROVAL_AUTO => __('Sofort veröffentlichen', 'wpsg')
+], $this->get_option('wpsg_mod_reviews_approval')); ?>