From ece573ed29ae37ea6d4fefafc61b41f6bd5249e2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patrick=20M=C3=BCller?= <p.mueller@pcsg.de> Date: Fri, 7 Jul 2017 12:47:09 +0200 Subject: [PATCH] refactor: temp commit (UserProfile) --- ajax/memberships/users/getList.php | 2 +- ajax/memberships/users/getSessionUserData.php | 46 +++++ ajax/memberships/users/update.php | 1 + bin/classes/MembershipUsers.js | 14 ++ bin/controls/profile/UserProfile.css | 11 ++ bin/controls/profile/UserProfile.html | 10 + bin/controls/profile/UserProfile.js | 184 ++++++++++++++++++ bin/membership.php | 71 ------- composer.json | 47 +++-- database.xml | 1 - intranet.xml | 6 + locale.xml | 126 ++++++++++-- settings.xml | 76 ++++++++ .../Memberships/Users/CancelVerification.php | 145 ++++++++++++++ src/QUI/Memberships/Users/Handler.php | 36 +++- src/QUI/Memberships/Users/MembershipUser.php | 122 +++++++----- templates/mail_manualextend.html | 11 ++ 17 files changed, 752 insertions(+), 157 deletions(-) create mode 100644 ajax/memberships/users/getSessionUserData.php create mode 100644 bin/controls/profile/UserProfile.css create mode 100644 bin/controls/profile/UserProfile.html create mode 100644 bin/controls/profile/UserProfile.js delete mode 100644 bin/membership.php create mode 100644 intranet.xml create mode 100644 src/QUI/Memberships/Users/CancelVerification.php create mode 100644 templates/mail_manualextend.html diff --git a/ajax/memberships/users/getList.php b/ajax/memberships/users/getList.php index c903e5f..3c917c2 100644 --- a/ajax/memberships/users/getList.php +++ b/ajax/memberships/users/getList.php @@ -43,7 +43,7 @@ function ($membershipId, $searchParams) { } /** @var \QUI\Memberships\Users\MembershipUser $TEST */ -// $TEST = $MembershipUsers->getChild(22); +// $TEST = $MembershipUsers->getChild(26); // $TEST->startManualCancel(); $Grid = new Grid($searchParams); diff --git a/ajax/memberships/users/getSessionUserData.php b/ajax/memberships/users/getSessionUserData.php new file mode 100644 index 0000000..c84a472 --- /dev/null +++ b/ajax/memberships/users/getSessionUserData.php @@ -0,0 +1,46 @@ +<?php + +use QUI\Memberships\Users\Handler as MembershipUsersHandler; +use QUI\Memberships\Users\MembershipUser; + +/** + * Get all MembershipUser Objects data for the current session user + * + * @return array - view data for all relevant MembershipUser objects + */ +QUI::$Ajax->registerFunction( + 'package_quiqqer_memberships_ajax_memberships_users_getSessionUserData', + function () { + $SessionUser = QUI::getUserBySession(); + + try { + $membershipUsers = MembershipUsersHandler::getInstance() + ->getMembershipUsersByUserId($SessionUser->getId()); + + $data = array(); + + foreach ($membershipUsers as $MembershipUser) { + $data[] = $MembershipUser->getFrontendViewData(); + } + } catch (\Exception $Exception) { + QUI\System\Log::addError('AJAX :: package_quiqqer_memberships_ajax_memberships_users_getHistory'); + QUI\System\Log::writeException($Exception); + + QUI::getMessagesHandler()->addError( + QUI::getLocale()->get( + 'quiqqer/memberships', + 'message.ajax.general.error', + array( + 'error' => $Exception->getMessage() + ) + ) + ); + + return array(); + } + + return $data; + }, + array(), + 'Permission::checkAdminUser' +); diff --git a/ajax/memberships/users/update.php b/ajax/memberships/users/update.php index de3e2db..d9606e4 100644 --- a/ajax/memberships/users/update.php +++ b/ajax/memberships/users/update.php @@ -29,6 +29,7 @@ function ($membershipUserId, $attributes) { if ($oldVal != $v) { $updated[$k] = $oldVal . ' => ' . $v; + $MembershipUser->sendManualExtendMail(); } break; diff --git a/bin/classes/MembershipUsers.js b/bin/classes/MembershipUsers.js index d4cf6d6..8717452 100644 --- a/bin/classes/MembershipUsers.js +++ b/bin/classes/MembershipUsers.js @@ -105,6 +105,20 @@ define('package/quiqqer/memberships/bin/classes/MembershipUsers', [ }); }, + /** + * Get all Membership data for the current session user + * + * @return {Promise} + */ + getSessionUserData: function () { + return new Promise(function (resolve, reject) { + QUIAjax.get('package_quiqqer_memberships_ajax_memberships_users_getSessionUserData', resolve, { + 'package': pkg, + onError : reject + }) + }); + }, + /** * Get MembershipUser history * diff --git a/bin/controls/profile/UserProfile.css b/bin/controls/profile/UserProfile.css new file mode 100644 index 0000000..4674b95 --- /dev/null +++ b/bin/controls/profile/UserProfile.css @@ -0,0 +1,11 @@ +.quiqqer-memberships-profile-userprofile-status-cancelled { + color: red; +} + +.quiqqer-memberships-profile-userprofile-status-cancelled_start { + color: orange; +} + +.quiqqer-memberships-profile-userprofile-status-active { + color: green; +} \ No newline at end of file diff --git a/bin/controls/profile/UserProfile.html b/bin/controls/profile/UserProfile.html new file mode 100644 index 0000000..69ec9f6 --- /dev/null +++ b/bin/controls/profile/UserProfile.html @@ -0,0 +1,10 @@ +<h1>{{header}}</h1> +<table class="quiqqer-memberships-profile-userprofile-table"> + <thead> + <tr> + <th>{{headerMembership}}</th> + <th>{{headerMembershipData}}</th> + </tr> + </thead> + <tbody></tbody> +</table> \ No newline at end of file diff --git a/bin/controls/profile/UserProfile.js b/bin/controls/profile/UserProfile.js new file mode 100644 index 0000000..8ad2378 --- /dev/null +++ b/bin/controls/profile/UserProfile.js @@ -0,0 +1,184 @@ +/** + * UserProfile JavaScript Control + * + * View data from archived membership users + * + * @module package/quiqqer/memberships/bin/controls/profile/UserProfile + * @author www.pcsg.de (Patrick Müller) + * + * @require qui/controls/Control + * @require qui/controls/loader/Loader + * @require qui/controls/windows/Popup + * @require qui/controls/windows/Confirm + * @require qui/controls/buttons/Button + * @require utils/Controls + * @require controls/grid/Grid + * @require package/quiqqer/memberships/bin/Licenses + * @require package/quiqqer/memberships/bin/controls/LicenseBundles + * @require Locale + * @require Ajax + * @require Mustache + * @require text!package/quiqqer/memberships/bin/controls/profile/UserProfile.html + * @require css!package/quiqqer/memberships/bin/controls/profile/UserProfile.css + */ +define('package/quiqqer/memberships/bin/controls/profile/UserProfile', [ + + 'qui/controls/Control', + 'qui/controls/loader/Loader', + 'qui/controls/windows/Popup', + 'qui/controls/windows/Confirm', + 'qui/controls/buttons/Button', + + 'utils/Controls', + + 'package/quiqqer/memberships/bin/Memberships', + 'package/quiqqer/memberships/bin/MembershipUsers', + + 'Locale', + 'Ajax', + 'Mustache', + + 'text!package/quiqqer/memberships/bin/controls/profile/UserProfile.html', + 'css!package/quiqqer/memberships/bin/controls/profile/UserProfile.css' + +], function (QUIControl, QUILoader, QUIPopup, QUIConfirm, QUIButton, + QUIControlUtils, Memberships, MembershipUsersHandler, + QUILocale, QUIAjax, Mustache, template) { + "use strict"; + + var lg = 'quiqqer/memberships'; + + return new Class({ + + Extends: QUIControl, + Type : 'package/quiqqer/memberships/bin/controls/profile/UserProfile', + + Binds: [ + '$onInject', + '$onResize', + '$onCreate', + 'refresh', + '$build' + ], + + options: { + membershipId: false + }, + + initialize: function (options) { + this.parent(options); + + this.Loader = new QUILoader(); + this.$memberships = []; + + this.addEvents({ + onCreate: this.$onCreate, + onInject: this.$onInject, + onResize: this.$onResize + }); + }, + + /** + * Event: onCreate + */ + $onCreate: function () { + + }, + + /** + * Event: onImport + */ + $onInject: function () { + this.$Elm.addClass('quiqqer-memberships-membershipusersarchive'); + + var lgPrefix = 'controls.profile.userprofile.template.'; + + this.$Elm.set('html', Mustache.render(template, { + header : QUILocale.get(lg, lgPrefix + 'header'), + headerMembership : QUILocale.get(lg, lgPrefix + 'headerMembership'), + headerMembershipData: QUILocale.get(lg, lgPrefix + 'headerMembershipData') + })); + + this.Loader.inject(this.$Elm); + + this.refresh(); + }, + + /** + * event: onResize + */ + $onResize: function () { + // @todo + }, + + /** + * Refresh control data + */ + refresh: function () { + var self = this; + + this.Loader.show(); + + MembershipUsersHandler.getSessionUserData().then(function (memberships) { + self.Loader.hide(); + self.$memberships = memberships; + self.$build(); + }); + }, + + /** + * Fill table with membership data + */ + $build: function () { + var self = this; + var lgPrefix = 'controls.profile.userprofile.datatable.'; + var TableBodyElm = this.$Elm.getElement( + '.quiqqer-memberships-profile-userprofile-table tbody' + ); + + for (var i = 0, len = this.$memberships.length; i < len; i++) { + var Membership = this.$memberships[i]; + var Row = new Element('tr').inject(TableBodyElm); + + // Membership title and description + new Element('td', { + html: '<h2>' + Membership.membershipTitle + '</h2>' + + '<p>' + Membership.membershipShort + '</p>' + }).inject(Row); + + // Membership data (dates and status) + var status = 'active'; + + if (Membership.cancelled) { + status = 'cancelled'; + } else if (Membership.cancelDate) { + status = 'cancelled_start'; + } + + new Element('td', { + html: '<table>' + + '<tr>' + + '<td>' + QUILocale.get(lg, lgPrefix + 'labelAddedDate') + '</td>' + + '<td>' + Membership.addedDate + '</td>' + + '</tr>' + + '<tr>' + + '<td>' + QUILocale.get(lg, lgPrefix + 'labelStatus') + '</td>' + + '<td><span class="quiqqer-memberships-profile-userprofile-status-' + status + '">' + + QUILocale.get(lg, lgPrefix + 'status.' + status) + '</span></td>' + + '</tr>' + + '</table>' + }).inject(Row); + } + }, + + $getCancelBtn: function() + { + + }, + + $getAbortCancelBtn: function() + { + + } + }); +}); diff --git a/bin/membership.php b/bin/membership.php deleted file mode 100644 index 29726c9..0000000 --- a/bin/membership.php +++ /dev/null @@ -1,71 +0,0 @@ -<?php - -use QUI\Memberships\Users\Handler as MembershipUsersHandler; - -define('QUIQQER_SYSTEM', true); - -require dirname(__FILE__, 4) . '/header.php'; - -/** - * Send 401 status code if anything goes wrong - */ -function send401() -{ - $Response = QUI::getGlobalResponse(); - $Response->setStatusCode(401, 'Unauthorized or malformed request'); - $Response->send(); - - exit; -} - -/** - * Cancel membership - */ -function cancel() -{ - // @todo check if user is logged in? - - $Engine = QUI::getTemplateManager()->getEngine(); - $Engine->assign(array( - 'error' => false - )); - - try { - $MembershipUsers = MembershipUsersHandler::getInstance(); - /** @var \QUI\Memberships\Users\MembershipUser $MembershipUser */ - $MembershipUser = $MembershipUsers->getChild((int)$_REQUEST['mid']); - $MembershipUser->confirmManualCancel($_REQUEST['hash']); - } catch (QUI\Memberships\Exception $Exception) { - $Engine->assign(array( - 'error' => true, - 'errorMessage' => $Exception->getMessage() - )); - } catch (\Exception $Exception) { - send401(); - } - - $template = $Engine->fetch(dirname(__FILE__, 2) . '/templates/cancel_confirm.html'); - - \QUI\System\Log::writeRecursive($template); - - echo $template; - exit; -} - -if (empty($_REQUEST['mid']) - || empty($_REQUEST['hash']) - || empty($_REQUEST['action']) -) { - send401(); -} - -switch ($_REQUEST['action']) { - case 'confirmManualCancel': - cancel(); - break; - - default: - send401(); -} - -exit; diff --git a/composer.json b/composer.json index 1e4c3cb..dc7ba8a 100644 --- a/composer.json +++ b/composer.json @@ -1,28 +1,27 @@ { - "name" : "quiqqer/memberships", - "type" : "quiqqer-module", - "description" : "Mitgliedschafen für ERP", - "version" : "dev-dev", - "license" : "GPL-3.0+", - "authors" : [ - { - "name": "Patrick Müller", - "email": "p.mueller@pcsg.de", - "homepage": "http://www.pcsg.de", - "role": "Developer" - } - ], - "support" : { - "email": "support@pcsg.de", - "url": "http://www.pcsg.de" - }, - "require": { - + "name": "quiqqer/memberships", + "type": "quiqqer-module", + "description": "Mitgliedschafen für ERP", + "version": "dev-dev", + "license": "GPL-3.0+", + "authors": [ + { + "name": "Patrick Müller", + "email": "p.mueller@pcsg.de", + "homepage": "http://www.pcsg.de", + "role": "Developer" } - ,"autoload": { - "psr-4": { + ], + "support": { + "email": "support@pcsg.de", + "url": "http://www.pcsg.de" + }, + "require": { + "quiqqer/verification": "dev-dev" + }, + "autoload": { + "psr-4": { "QUI\\Memberships\\": "src/QUI/Memberships" - } -} - + } + } } diff --git a/database.xml b/database.xml index 1c7a6f1..75cf91f 100644 --- a/database.xml +++ b/database.xml @@ -30,7 +30,6 @@ <field type="TIMESTAMP NULL DEFAULT NULL">endDate</field> <field type="SMALLINT UNSIGNED NOT NULL DEFAULT 0">extendCounter</field> <field type="TIMESTAMP NULL DEFAULT NULL">cancelDate</field> - <field type="VARCHAR(255) NULL">cancelHash</field> <field type="TINYINT(1) NOT NULL DEFAULT 0">cancelled</field> <field type="TINYINT(1) NOT NULL DEFAULT 0">archived</field> <field type="TIMESTAMP NULL DEFAULT NULL">archiveDate</field> diff --git a/intranet.xml b/intranet.xml new file mode 100644 index 0000000..2e601af --- /dev/null +++ b/intranet.xml @@ -0,0 +1,6 @@ +<?xml version="1.0" encoding="UTF-8"?> +<menu> + <item require="package/quiqqer/memberships/bin/controls/profile/UserProfile" icon="fa fa-id-card-o" name="memberships-profile"> + <locale group="quiqqer/memberships" var="profile.button.text" /> + </item> +</menu> diff --git a/locale.xml b/locale.xml index 35e81b4..7360d07 100644 --- a/locale.xml +++ b/locale.xml @@ -66,12 +66,12 @@ <en><![CDATA[Determines how the runtime of a membership is extended for a user when the user enters the membership even though he is already an active member.]]></en> </locale> <locale name="settings.extendMode.option.reset"> - <de><![CDATA[Start- und End-Zeitpunkt werden neu gesetzt]]></de> - <en><![CDATA[Start and end times are reset]]></en> + <de><![CDATA[Start- und Endzeitpunkt werden neu gesetzt, als wäre der Benutzer neu in die Mitgliedschaft gekommen]]></de> + <en><![CDATA[The start and end times are reset as if the user had entered the membership]]></en> </locale> <locale name="settings.extendMode.option.prolong"> - <de><![CDATA[Start-Zeitpunkt bleibt erhalten, nur End-Zeitpunkt wird neu gesetzt]]></de> - <en><![CDATA[Start time is retained, only end time is reset]]></en> + <de><![CDATA[Endzeitpunkt wird um die Laufzeit der Mitgliedschaft verlängert]]></de> + <en><![CDATA[End time is extended by the duration of the membership]]></en> </locale> <locale name="settings.durationMode.title"> <de><![CDATA[Genauigkeit von Laufzeiten]]></de> @@ -89,6 +89,50 @@ <de><![CDATA[Exakt (Laufzeiten werden sekundengenau berechnet)]]></de> <en><![CDATA[Exact (duration is calculated by the second)]]></en> </locale> + <locale name="settings.cancelDuration.title"> + <de><![CDATA[Gültigkeitsdauer f. Kündigungs-Bestätigungs-Link]]></de> + <en><![CDATA[Duration of cancellation confirmation links]]></en> + </locale> + <locale name="settings.cancelDuration.description" html="true"> + <de><![CDATA[Bestimmt, wie lange der Link zur Bestätigung einer Mitgliedschafts-Kündigung gültig bleibt. Angabe in <b>Minuten</b>.]]></de> + <en><![CDATA[Determines how long the link to confirm a membership termination remains valid (<b>minutes</b>).]]></en> + </locale> + <locale name="settings.sendAutoExtendMail.title"> + <de><![CDATA[E-Mail bei automatischer Verlängerung]]></de> + <en><![CDATA[Mail on auto extension]]></en> + </locale> + <locale name="settings.sendAutoExtendMail.description"> + <de><![CDATA[Bei einer automatischen Verlängerung der Mitgliedschaft wird eine E-Mail an den Benutzer gesendet]]></de> + <en><![CDATA[An automatic extension of the membership will send an e-mail to the user]]></en> + </locale> + <locale name="settings.sendManualExtendMail.title"> + <de><![CDATA[E-Mail bei manueller Verlängerung]]></de> + <en><![CDATA[Mail on manual extension]]></en> + </locale> + <locale name="settings.sendManualExtendMail.description"> + <de><![CDATA[Bei einer manuellen Verlängerung der Mitgliedschaft wird eine E-Mail an den Benutzer gesendet. Manuelle Verlängerung = Bearbeitung des End-Datums einer Mitgliedschaft durch einen Administrator oder das erneute Hinzufügen eines Benutzers zu einer Mitgliedschaft, in der er sich bereits befindet.]]></de> + <en><![CDATA[A manual extension of the membership will send an e-mail to the user. Manual Extension = The end date of a membership is edited by an administrator or a user is re-added to a membership that he already is in.]]></en> + </locale> + <locale name="settings.dateformat.title"> + <de><![CDATA[Datumsformatierung]]></de> + <en><![CDATA[Date format]]></en> + </locale> + <locale name="settings.dateFormatShort.title"> + <de><![CDATA[Datumsformatierung (kurz)]]></de> + <en><![CDATA[Date format (short)]]></en> + </locale> + <locale name="settings.dateFormatShort.description" html="true"> + <de><![CDATA[Datumsformatierung für Datumsangaben in kurzem Format. Das kurze Datumsformat wird in Nachrichten an den Benutzer verwendet, wenn die <b>Genauigkeit von Laufzeiten</b> auf <b>Tag-genau</b> gestellt ist.<br><br>Für verfügbare Platzhalter siehe <a href="http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters" target="_blank">http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters</a>]]></de> + <en><![CDATA[Date format for dates in short format. The short format is used in messages sent to the user if the <b>Accuracy of membership duration</b> setting is set to <b>Day-exact</b>.<br><br>For available placeholder see: <a href="http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters" target="_blank">http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters</a>]]></en> + </locale> + <locale name="settings.dateFormatLong.title"> + <de><![CDATA[Datumsformatierung (lang)]]></de> + <en><![CDATA[Date format (long)]]></en> + </locale> + <locale name="settings.dateFormatLong.description" html="true"> + <de><![CDATA[Datumsformatierung für Datumsangaben in langem Format. Das lange Datumsformat wird in Nachrichten an den Benutzer verwendet, wenn die <b>Genauigkeit von Laufzeiten</b> auf <b>Exakt</b> gestellt ist.<br><br>Für verfügbare Platzhalter siehe <a href="http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters" target="_blank">http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters</a>]]></de> + <en><![CDATA[Date format for dates in long format. The short format is used in messages sent to the user if the <b>Accuracy of membership duration</b> setting is set to <b>Exact</b>.<br><br>For available placeholder see: <a href="http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters" target="_blank">http://php.net/manual/de/function.strftime.php#refsect1-function.strftime-parameters</a>]]></en> + </locale> <!-- Cron --> <locale name="cron.checkMembershipUsers.title"> @@ -100,6 +144,12 @@ <en><![CDATA[Checks for each membership whether it needs to be extended or terminated.]]></en> </locale> + <!-- Profile --> + <locale name="profile.button.text"> + <de><![CDATA[Mitgliedschaften]]></de> + <en><![CDATA[Memberships]]></en> + </locale> + </groups> <groups name="quiqqer/memberships" datatype="js"> @@ -600,6 +650,40 @@ <en><![CDATA[Send confirmation]]></en> </locale> + <!-- Control: profile/UserProfile --> + <locale name="controls.profile.userprofile.template.header"> + <de><![CDATA[Ihre Mitgliedschaften]]></de> + <en><![CDATA[Your memberships]]></en> + </locale> + <locale name="controls.profile.userprofile.template.headerMembership"> + <de><![CDATA[Mitgliedschaft]]></de> + <en><![CDATA[Membership]]></en> + </locale> + <locale name="controls.profile.userprofile.template.headerMembershipData"> + <de><![CDATA[Details]]></de> + <en><![CDATA[Details]]></en> + </locale> + <locale name="controls.profile.userprofile.datatable.labelAddedDate"> + <de><![CDATA[In Mitgliedschaft seit]]></de> + <en><![CDATA[In membership since]]></en> + </locale> + <locale name="controls.profile.userprofile.datatable.labelStatus"> + <de><![CDATA[Status]]></de> + <en><![CDATA[Status]]></en> + </locale> + <locale name="controls.profile.userprofile.datatable.status.cancelled"> + <de><![CDATA[gekündigt]]></de> + <en><![CDATA[cancelled]]></en> + </locale> + <locale name="controls.profile.userprofile.datatable.status.cancelled_start"> + <de><![CDATA[Kündigungsbestätigung ausstehend]]></de> + <en><![CDATA[Cancellation confirmation pending]]></en> + </locale> + <locale name="controls.profile.userprofile.datatable.status.active"> + <de><![CDATA[aktiv]]></de> + <en><![CDATA[active]]></en> + </locale> + <!-- Templates --> <locale name="templates.mail.greeting"> <de><![CDATA[Hallo [name]!]]></de> @@ -621,14 +705,6 @@ <de><![CDATA[Hiermit bestätigen wir die Kündigung Ihrer Mitgliedschaft <b>[membershipTitle]</b> zum <b>[endDate]</b>. Bis zu diesem Zeitpunkt können Sie weiterhin alle Vorteile Ihrer Mitgliedschaft genießen.]]></de> <en><![CDATA[We hereby confirm the termination of your membership <b>[membershipTitle]</b> as of <b>[endDate]</b>. Until that time, you can continue to enjoy all the benefits of your membership.]]></en> </locale> - <locale name="templates.cancel.confirm.success"> - <de><![CDATA[Die Kündigung Ihrer Mitgliedschaft war erfolgreich!]]></de> - <en><![CDATA[The termination of your membership has been successful!]]></en> - </locale> - <locale name="templates.cancel.confirm.error"> - <de><![CDATA[Bei der Kündigung Ihrer Mitgliedschaft ist ein Fehler aufgetreten:<br><br>[errorMessage]]]></de> - <en><![CDATA[An error occurred during the termination of your membership:<br><br>[errorMessage]]]></en> - </locale> <locale name="templates.mail.expired.subject" html="true"> <de><![CDATA[Ablauf Ihrer Mitgliedschaft]]></de> <en><![CDATA[Expiration of your membership]]></en> @@ -645,6 +721,32 @@ <de><![CDATA[Ihre Mitgliedschaft <b>[membershipTitle]</b> wurde automatisch bis zum <b>[endDate]</b> verlängert.]]></de> <en><![CDATA[Your membership <b>[membershipTitle]</b> has been extended automatically until <b>[endDate]</b>.]]></en> </locale> + <locale name="templates.mail.manualextend.subject" html="true"> + <de><![CDATA[Verlängerung Ihrer Mitgliedschaft]]></de> + <en><![CDATA[Extension of your membership]]></en> + </locale> + <locale name="templates.mail.manualextend.body" html="true"> + <de><![CDATA[Ihre Mitgliedschaft <b>[membershipTitle]</b> wurde bis zum <b>[endDate]</b> verlängert.]]></de> + <en><![CDATA[Your membership <b>[membershipTitle]</b> has been extended until <b>[endDate]</b>.]]></en> + </locale> + + <!-- CancelVerification --> + <locale name="verification.cancel.success"> + <de><![CDATA[Die Kündigung Ihrer Mitgliedschaft war erfolgreich! Sie erhalten noch eine separate E-Mail mit der Bestätigung Ihrer Kündigung.]]></de> + <en><![CDATA[The termination of your membership has been successful! You will receive a separate e-mail with confirmation of your termination.]]></en> + </locale> + <locale name="verification.cancel.error.general"> + <de><![CDATA[Bei der Kündigung Ihrer Mitgliedschaft ist ein Fehler aufgetreten. Bitte starten Sie den Kündigungsvorgang erneut oder kontaktieren Sie einen Administrator.]]></de> + <en><![CDATA[There was an error while canceling your membership. Please restart the termination process or contact an administrator.]]></en> + </locale> + <locale name="verification.cancel.error.expired"> + <de><![CDATA[Die Gültigkeit Ihres Kündigungs-Links ist leider abgelaufen. Bitte starten sie den Kündigungsvorgang erneut.]]></de> + <en><![CDATA[The validity of your termination link has unfortunately expired. Please restart the termination process.]]></en> + </locale> + <locale name="verification.cancel.error.already_cancelled"> + <de><![CDATA[Die Mitgliedschaft wurde bereits gekündigt.]]></de> + <en><![CDATA[The membership has already been cancelled.]]></en> + </locale> </groups> </locales> diff --git a/settings.xml b/settings.xml index 8ddb590..a30b622 100644 --- a/settings.xml +++ b/settings.xml @@ -16,6 +16,30 @@ <type><![CDATA[string]]></type> <defaultvalue>reset</defaultvalue> </conf> + <conf name="cancelDuration"> + <type><![CDATA[integer]]></type> + <defaultvalue>1440</defaultvalue> + </conf> + <conf name="sendAutoExtendMail"> + <type><![CDATA[bool]]></type> + <defaultvalue>1</defaultvalue> + </conf> + <conf name="sendManualExtendMail"> + <type><![CDATA[bool]]></type> + <defaultvalue>1</defaultvalue> + </conf> + </section> + + <section name="date_formats_short"> + <conf name="??"> + <type><![CDATA[string]]></type> + </conf> + </section> + + <section name="date_formats_long"> + <conf name="??"> + <type><![CDATA[string]]></type> + </conf> </section> </config> @@ -68,6 +92,58 @@ <locale group="quiqqer/memberships" var="settings.durationMode.option.exact"/> </option> </select> + + <input conf="membershipusers.cancelDuration" type="number"> + <text> + <locale group="quiqqer/memberships" var="settings.cancelDuration.title"/> + </text> + <description> + <locale group="quiqqer/memberships" var="settings.cancelDuration.description"/> + </description> + </input> + + <input conf="membershipusers.sendAutoExtendMail" type="checkbox"> + <text> + <locale group="quiqqer/memberships" var="settings.sendAutoExtendMail.title"/> + </text> + <description> + <locale group="quiqqer/memberships" var="settings.sendAutoExtendMail.description"/> + </description> + </input> + + <input conf="membershipusers.sendManualExtendMail" type="checkbox"> + <text> + <locale group="quiqqer/memberships" var="settings.sendManualExtendMail.title"/> + </text> + <description> + <locale group="quiqqer/memberships" var="settings.sendManualExtendMail.description"/> + </description> + </input> + + </settings> + + <settings title="dateformat" name="dateformat"> + <title> + <locale group="quiqqer/memberships" var="settings.dateformat.title"/> + </title> + + <input conf="date_formats_short" type="text" data-qui="controls/system/AvailableLanguages"> + <text> + <locale group="quiqqer/memberships" var="settings.dateFormatShort.title"/> + </text> + <description> + <locale group="quiqqer/memberships" var="settings.dateFormatShort.description"/> + </description> + </input> + + <input conf="date_formats_long" type="text" data-qui="controls/system/AvailableLanguages"> + <text> + <locale group="quiqqer/memberships" var="settings.dateFormatLong.title"/> + </text> + <description> + <locale group="quiqqer/memberships" var="settings.dateFormatLong.description"/> + </description> + </input> </settings> </category> diff --git a/src/QUI/Memberships/Users/CancelVerification.php b/src/QUI/Memberships/Users/CancelVerification.php new file mode 100644 index 0000000..9b4d8e1 --- /dev/null +++ b/src/QUI/Memberships/Users/CancelVerification.php @@ -0,0 +1,145 @@ +<?php + +namespace QUI\Memberships\Users; + +use QUI\Verification\VerificationInterface; +use QUI\Memberships\Users\Handler as MembershipUsersHandler; +use QUI\Verification\Verifier; +use QUI; + +class CancelVerification implements VerificationInterface +{ + /** + * Verification identifier + * + * @var string + */ + protected $identifier = null; + + /** + * CancelVerification constructor. + * + * @param int $membershipUserId + */ + public function __construct($membershipUserId) + { + $this->identifier = $membershipUserId; + } + + /** + * Get a unique identifier that identifies this Verification + * + * @return string + */ + public function getIdentifier() + { + return $this->identifier; + } + + /** + * Get the duration of a Verification (minutes) + * + * @return int|false - duration in minutes; + * if this method returns false use the module setting default value + */ + public static function getValidDuration() + { + return (int)MembershipUsersHandler::getSetting('cancelDuration'); + } + + /** + * Execute this method on successful verification + * + * @param string $identifier - Unique Verification identifier + * @return void + */ + public static function onSuccess($identifier) + { + /** @var MembershipUser $MembershipUser */ + $MembershipUser = MembershipUsersHandler::getInstance()->getChild((int)$identifier); + $MembershipUser->confirmManualCancel(); + } + + /** + * Execute this method on unsuccessful verification + * + * @param string $identifier - Unique Verification identifier + * @return void + */ + public static function onError($identifier) + { + // nothing + } + + /** + * This message is displayed to the user on successful verification + * + * @param string $identifier - Unique Verification identifier + * @return string + */ + public static function getSuccessMessage($identifier) + { + return QUI::getLocale()->get( + 'quiqqer/memberships', + 'verification.cancel.success' + ); + } + + /** + * This message is displayed to the user on unsuccessful verification + * + * @param string $identifier - Unique Verification identifier + * @param string $reason - The reason for the error (see \QUI\Verification\Verifier::REASON_) + * @return string + */ + public static function getErrorMessage($identifier, $reason) + { + switch ($reason) { + case Verifier::ERROR_REASON_EXPIRED: + $msg = QUI::getLocale()->get( + 'quiqqer/memberships', + 'verification.cancel.error.expired' + ); + break; + + case Verifier::ERROR_REASON_ALREADY_VERIFIED: + $msg = QUI::getLocale()->get( + 'quiqqer/memberships', + 'verification.cancel.error.already_cancelled' + ); + break; + + default: + $msg = QUI::getLocale()->get( + 'quiqqer/memberships', + 'verification.cancel.error.general' + ); + } + + return $msg; + } + + /** + * Automatically redirect the user to this URL on successful verification + * + * @param string $identifier - Unique Verification identifier + * @return string|false - If this method returns false, no redirection takes place + */ + public static function getOnSuccessRedirectUrl($identifier) + { + return false; + } + + /** + * Automatically redirect the user to this URL on unsuccessful verification + * + * Hint: This requires that an active Verification with the given identifier exists! + * + * @param string $identifier - Unique Verification identifier + * @return string|false - If this method returns false, no redirection takes place + */ + public static function getOnErrorRedirectUrl($identifier) + { + return false; + } +} diff --git a/src/QUI/Memberships/Users/Handler.php b/src/QUI/Memberships/Users/Handler.php index c985f5c..916dc75 100644 --- a/src/QUI/Memberships/Users/Handler.php +++ b/src/QUI/Memberships/Users/Handler.php @@ -134,6 +134,41 @@ public function getUserIdsByMembershipId($membershipId) return $membershipUserIds; } + /** + * Get all MembershipUser objects by userId + * + * @param int $userId - QUIQQER User ID + * @param bool $includeArchived (optional) - include archived MembershipUsers + * @return MembershipUser[] + */ + public function getMembershipUsersByUserId($userId, $includeArchived = false) + { + $where = array( + 'userId' => $userId, + 'archived' => 0 + ); + + if ($includeArchived === true) { + unset($where['archived']); + } + + $result = QUI::getDataBase()->fetch(array( + 'select' => array( + 'id' + ), + 'from' => self::getDataBaseTableName(), + 'where' => $where + )); + + $membershipUsers = array(); + + foreach ($result as $row) { + $membershipUsers[] = self::getChild($row['id']); + } + + return $membershipUsers; + } + // /** // * Get membership // * @@ -204,7 +239,6 @@ public function getChildAttributes() 'endDate', 'archived', 'history', - 'cancelHash', 'cancelDate', 'cancelled', 'archiveReason', diff --git a/src/QUI/Memberships/Users/MembershipUser.php b/src/QUI/Memberships/Users/MembershipUser.php index 57e2a56..12cbd7f 100644 --- a/src/QUI/Memberships/Users/MembershipUser.php +++ b/src/QUI/Memberships/Users/MembershipUser.php @@ -9,6 +9,7 @@ use QUI\Memberships\Utils; use QUI\Mail\Mailer; use QUI\Permissions\Permission; +use QUI\Verification\Verifier; /** * Class MembershipUser @@ -61,6 +62,7 @@ public function extend($auto = true) $Membership = $this->getMembership(); $extendMode = MembershipUsersHandler::getSetting('extendMode'); + // Calculate new start and/or end time if ($auto || $extendMode === 'reset') { $start = time(); $extendCounter = $this->getAttribute('extendCounter'); @@ -87,11 +89,57 @@ public function extend($auto = true) $this->addHistoryEntry(MembershipUsersHandler::HISTORY_TYPE_EXTENDED, json_encode($historyData)); $this->update(); - // send autoextend mail - $subject = $this->getUser()->getLocale()->get('quiqqer/memberships', 'templates.mail.autoextend.subject'); + // send mail + if ($auto) { + $this->sendAutoExtendMail(); + } else { + $this->sendManualExtendMail(); + } + } + + /** + * Send mail to the user if the membership is extended automatically + * + * @return void + */ + protected function sendAutoExtendMail() + { + $sendMail = MembershipUsersHandler::getSetting('sendAutoExtendMail'); + + if (!$sendMail) { + return; + } + + $subject = $this->getUser()->getLocale()->get( + 'quiqqer/memberships', 'templates.mail.autoextend.subject' + ); + $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_autoextend.html'); } + /** + * Send mail to the user if the membership is extended manually + * + * Manually = Either by admin edit or if the user is re-added to the membership + * although he already is a member + * + * @return void + */ + public function sendManualExtendMail() + { + $sendMail = MembershipUsersHandler::getSetting('sendManualExtendMail'); + + if (!$sendMail) { + return; + } + + $subject = $this->getUser()->getLocale()->get( + 'quiqqer/memberships', 'templates.mail.manualextend.subject' + ); + + $this->sendMail($subject, dirname(__FILE__, 5) . '/templates/mail_manualextend.html'); + } + /** * Expires this memberships user * @@ -122,21 +170,8 @@ public function startManualCancel() } $cancelDate = Utils::getFormattedTimestamp(); - $cancelHash = md5(openssl_random_pseudo_bytes(256)); - $cancelUrl = QUI::getRewrite()->getProject()->getVHost(true); - $cancelUrl .= URL_OPT_DIR . 'quiqqer/memberships/bin/membership.php'; - - $params = array( - 'mid' => $this->id, - 'hash' => $cancelHash, - 'action' => 'confirmManualCancel' - ); - - $cancelUrl .= '?' . http_build_query($params); - // generate random hash $this->setAttributes(array( - 'cancelHash' => $cancelHash, 'cancelDate' => $cancelDate )); @@ -145,13 +180,15 @@ public function startManualCancel() // save cancel hash and date to database $this->update(); + $CancelVerification = new CancelVerification($this->id); + // send cancellation mail $this->sendMail( QUI::getLocale()->get('quiqqer/memberships', 'templates.mail.startcancel.subject'), dirname(__FILE__, 5) . '/templates/mail_startcancel.html', array( 'cancelDate' => $cancelDate, - 'cancelUrl' => $cancelUrl + 'cancelUrl' => Verifier::startVerification($CancelVerification) ) ); } @@ -159,34 +196,14 @@ public function startManualCancel() /** * Confirm membership cancellation * - * @param string $confirmHash - cancel hash * @return void * * @throws QUI\Memberships\Exception */ - public function confirmManualCancel($confirmHash) + public function confirmManualCancel() { if ($this->isCancelled()) { - throw new QUI\Memberships\Exception(array( - 'quiqqer/memberships', - 'exception.users.membershipuser.confirmManualCancel.already.cancelled' - )); - } - - $cancelHash = $this->getAttribute('cancelHash'); - -// if (empty($cancelHash)) { -// throw new QUI\Memberships\Exception(array( -// 'quiqqer/memberships', -// 'exception.users.membershipuser.confirmManualCancel.no.hash' -// )); -// } - - if ($confirmHash !== $cancelHash) { - throw new QUI\Memberships\Exception(array( - 'quiqqer/memberships', - 'exception.users.membershipuser.confirmManualCancel.hash.mismatch' - )); + return; } $this->setAttributes(array( @@ -426,21 +443,30 @@ public function getHistory() protected function formatDate($date) { $Locale = $this->getUser()->getLocale(); + $lang = $Locale->getCurrent(); $durationMode = MembershipsHandler::getSetting('durationMode'); - $timestamp = strtotime($date); + $Conf = QUI::getPackage('quiqqer/memberships')->getConfig(); switch ($durationMode) { case 'day': - $dayDate = date('Y-m-d', $timestamp); - $formattedDate = $Locale->formatDate(strtotime($dayDate)); + $dateFormat = $Conf->get('date_formats_short', $lang); + + // fallback to default value + if (empty($dateFormat)) { + $dateFormat = '%D'; + } break; default: - $minuteDate = date('Y-m-d H:i', $timestamp); - $formattedDate = $Locale->formatDate(strtotime($minuteDate)); + $dateFormat = $Conf->get('date_formats_long', $lang); + + // fallback to default value + if (empty($dateFormat)) { + $dateFormat = '%D %H:%M'; + } } - return $formattedDate; + return $Locale->formatDate(strtotime($date), $dateFormat); } /** @@ -452,19 +478,21 @@ public function getFrontendViewData() { $QuiqqerUser = $this->getUser(); $Membership = $this->getMembership(); + $Locale = $QuiqqerUser->getLocale(); return array( 'id' => $this->getId(), 'userId' => $QuiqqerUser->getId(), 'membershipId' => $Membership->getId(), - 'membershipTitle' => $Membership->getTitle(), + 'membershipTitle' => $Membership->getTitle($Locale), + 'membershipShort' => $Membership->getDescription($Locale), 'username' => $QuiqqerUser->getUsername(), 'fullName' => $QuiqqerUser->getName(), 'addedDate' => $this->formatDate($this->getAttribute('addedDate')), 'beginDate' => $this->formatDate($this->getAttribute('beginDate')), 'endDate' => $this->formatDate($this->getAttribute('endDate')), - 'archived' => $this->isArchived(), - 'archiveReason' => $this->getAttribute('archiveReason'), +// 'archived' => $this->isArchived(), +// 'archiveReason' => $this->getAttribute('archiveReason'), 'cancelled' => $this->isCancelled() ); } diff --git a/templates/mail_manualextend.html b/templates/mail_manualextend.html new file mode 100644 index 0000000..a9e3963 --- /dev/null +++ b/templates/mail_manualextend.html @@ -0,0 +1,11 @@ +<h1> + {locale group="quiqqer/memberships" value="templates.mail.greeting" Locale=$Locale + name=$MembershipUser->getUser()->getName()} +</h1> + +<p> + {locale group="quiqqer/memberships" value="templates.mail.manualextend.body" Locale=$Locale + membershipTitle=$MembershipUser->getMembership()->getTitle() + endDate=$data['endDate'] + } +</p> \ No newline at end of file -- GitLab