<canbeedited>1</canbeedited>
<canbedisabled>1</canbedisabled>
</cronjob>
+
+ <cronjob name="com.woltlab.wcf.expiringPaidSubscriptionUser">
+ <classname>wcf\system\cronjob\ExpiringPaidSubscriptionUserCronjob</classname>
+ <description>Sends notifications about expiring paid subscriptions</description>
+ <description language="de">Sendet Benachrichtigungen über ablaufende bezahlte Mitgliedschaften</description>
+ <startminute>0</startminute>
+ <starthour>2</starthour>
+ <startdom>*</startdom>
+ <startmonth>*</startmonth>
+ <startdow>*</startdow>
+ <canbeedited>1</canbeedited>
+ <canbedisabled>1</canbedisabled>
+ </cronjob>
</import>
<delete>
<classname>wcf\system\payment\method\PaypalPaymentMethod</classname>
</type>
+ <!-- paid subscriptions -->
<type>
<name>com.woltlab.wcf.payment.type.paidSubscription</name>
<definitionname>com.woltlab.wcf.payment.type</definitionname>
<classname>wcf\system\payment\type\PaidSubscriptionPaymentType</classname>
</type>
+ <type>
+ <name>com.woltlab.wcf.paidSubscription.user</name>
+ <definitionname>com.woltlab.wcf.notification.objectType</definitionname>
+ <classname>wcf\system\user\notification\object\type\PaidSubscriptionUserUserNotificationObjectType</classname>
+ <category>com.woltlab.wcf.user</category>
+ </type>
+ <!-- /paid subscriptions -->
<!-- bulk processable objects -->
<type>
<preset>1</preset>
<permissions>mod.general.canUseModeration</permissions>
</event>
+
+ <event>
+ <name>expiring</name>
+ <objecttype>com.woltlab.wcf.paidSubscription.user</objecttype>
+ <classname>wcf\system\user\notification\event\PaidSubscriptionUserUserNotificationEvent</classname>
+ <options>module_paid_subscription</options>
+ </event>
</import>
</data>
<?php
namespace wcf\data\paid\subscription;
+use wcf\data\ITitledObject;
use wcf\data\object\type\ObjectTypeCache;
use wcf\data\DatabaseObject;
use wcf\system\html\output\HtmlOutputProcessor;
* @property-read string $groupIDs comma-separated list with the ids of the user groups for which the subscription pays membership
* @property-read string $excludedSubscriptionIDs comma-separated list with the ids of paid subscriptions which prohibit purchase of this paid subscription
*/
-class PaidSubscription extends DatabaseObject {
+class PaidSubscription extends DatabaseObject implements ITitledObject {
/**
* Returns list of purchase buttons.
*
return $this->description;
}
+
+ /**
+ * @see ITitledObject::getTitle()
+ * @since 3.1
+ */
+ public function getTitle() {
+ return WCF::getLanguage()->get($this->title);
+ }
}
* @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
* @package WoltLabSuite\Core\Data\Paid\Subscription\User
*
- * @property-read integer $subscriptionUserID unique id of the paid subscription-user-association
- * @property-read integer $subscriptionID id of the paid subscription the paid subscription-user-association belongs to
- * @property-read integer $userID id of the user the paid subscription-user-association belongs to
- * @property-read integer $startDate timestamp at which the paid subscription started
- * @property-read integer $endDate timestamp at which the paid subscription ended or will end
- * @property-read integer $isActive is `1` if the user's paid subscription is currently active and thus not expired, otherwise `0`
+ * @property-read integer $subscriptionUserID unique id of the paid subscription-user-association
+ * @property-read integer $subscriptionID id of the paid subscription the paid subscription-user-association belongs to
+ * @property-read integer $userID id of the user the paid subscription-user-association belongs to
+ * @property-read integer $startDate timestamp at which the paid subscription started
+ * @property-read integer $endDate timestamp at which the paid subscription ended or will end
+ * @property-read integer $isActive is `1` if the user's paid subscription is currently active and thus not expired, otherwise `0`
+ * @property-read integer $sentExpirationNotification is `1` if the user has been notified that the paid subscription is expiring
*/
class PaidSubscriptionUser extends DatabaseObject {
/**
$subscriptionUser->update([
'endDate' => $endDate,
- 'isActive' => 1
+ 'isActive' => 1,
+ 'sentExpirationNotification' => 0
]);
if (!$subscriptionUser->isActive) {
--- /dev/null
+<?php
+namespace wcf\system\cronjob;
+use wcf\data\cronjob\Cronjob;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserEditor;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\user\notification\object\PaidSubscriptionUserUserNotificationObject;
+use wcf\system\user\notification\UserNotificationHandler;
+use wcf\system\WCF;
+
+/**
+ * Sends notifications for expiring paid subscriptions.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\Cronjob
+ * @since 3.1
+ */
+class ExpiringPaidSubscriptionUserCronjob extends AbstractCronjob {
+ /**
+ * @inheritDoc
+ */
+ public function execute(Cronjob $cronjob) {
+ parent::execute($cronjob);
+
+ // determine when the notification will be send prior to its expiration
+ $conditionBuilder = new PreparedStatementConditionBuilder(false, 'OR');
+
+ // one week before if the subscription lasts months or years (and not just days)
+ $conditionBuilder->add(
+ '(paid_subscription.subscriptionLengthUnit <> ? AND paid_subscription_user.endDate < ?)',
+ ['d', TIME_NOW + 7 * 24 * 3600]
+ );
+ // one week before if the subscription lasts for more than two weeks (2 * 7 days)
+ $conditionBuilder->add(
+ '(paid_subscription.subscriptionLengthUnit = ? AND paid_subscription.subscriptionLength > ? AND paid_subscription_user.endDate < ?)',
+ ['d', 2 * 7, TIME_NOW + 7 * 24 * 3600]
+ );
+ // two days before if the subscription lasts for less than two weeks (2 * 7 days)
+ $conditionBuilder->add(
+ '(paid_subscription.subscriptionLengthUnit = ? AND paid_subscription.subscriptionLength <= ? AND paid_subscription_user.endDate < ?)',
+ ['d', 2 * 7, TIME_NOW + 2 * 24 * 3600]
+ );
+
+ $paidSubscriptionUserList = new PaidSubscriptionUserList();
+ $paidSubscriptionUserList->sqlJoins .= " LEFT JOIN wcf".WCF_N."_paid_subscription paid_subscription ON (paid_subscription.subscriptionID = paid_subscription_user.subscriptionID)";
+ $paidSubscriptionUserList->getConditionBuilder()->add('paid_subscription_user.endDate <> ?', [0]);
+ $paidSubscriptionUserList->getConditionBuilder()->add('(' . $conditionBuilder . ')', $conditionBuilder->getParameters());
+ $paidSubscriptionUserList->getConditionBuilder()->add('paid_subscription_user.isActive = ?', [1]);
+ $paidSubscriptionUserList->getConditionBuilder()->add('paid_subscription_user.sentExpirationNotification = ?', [0]);
+ $paidSubscriptionUserList->readObjects();
+
+ foreach ($paidSubscriptionUserList as $paidSubscriptionUser) {
+ UserNotificationHandler::getInstance()->fireEvent(
+ 'expiring',
+ 'com.woltlab.wcf.paidSubscription.user',
+ new PaidSubscriptionUserUserNotificationObject($paidSubscriptionUser),
+ [$paidSubscriptionUser->userID]
+ );
+ }
+
+ // remember that notification has already been sent (or at least
+ // considered if the user does not want to receive notifications)
+ WCF::getDB()->beginTransaction();
+ foreach ($paidSubscriptionUserList as $paidSubscriptionUser) {
+ (new PaidSubscriptionUserEditor($paidSubscriptionUser))->update([
+ 'sentExpirationNotification' => 1
+ ]);
+ }
+ WCF::getDB()->commitTransaction();
+ }
+}
\ No newline at end of file
* indicates if the full difference is returned or just a rounded difference.
*
* Usage:
- * {$timestamp|dateDiff}
- * {"123456789"|dateDiff:$timestamp:$fullInverval}
+ * {$endTimestamp|dateDiff}
+ * {$endTimestamp|dateDiff:$startTimestamp:$fullInterval}
+ * {$endTimestamp|dateDiff:$startTimestamp:$fullInterval:$inSentence}
*
* @author Matthias Schmidt, Marcel Werk
* @copyright 2001-2017 WoltLab GmbH
$fullInterval = $tagArgs[2];
}
+ $inSentence = false;
+ if (isset($tagArgs[3])) {
+ $inSentence = $tagArgs[3];
+ }
+
$startTime = DateUtil::getDateTimeByTimestamp($tagArgs[1]);
$endTime = DateUtil::getDateTimeByTimestamp($tagArgs[0]);
- return DateUtil::formatInterval($endTime->diff($startTime), $fullInterval);
+ return DateUtil::formatInterval($endTime->diff($startTime), $fullInterval, $inSentence);
}
}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\event;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+use wcf\system\request\LinkHandler;
+use wcf\system\user\notification\object\PaidSubscriptionUserUserNotificationObject;
+use wcf\system\WCF;
+
+/**
+ * Notification event for followers.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\User\Notification\Event
+ * @since 3.1
+ *
+ * @method PaidSubscriptionUserUserNotificationObject getUserNotificationObject()
+ */
+class PaidSubscriptionUserUserNotificationEvent extends AbstractUserNotificationEvent {
+ /**
+ * @inheritDoc
+ */
+ public function getLink() {
+ return LinkHandler::getInstance()->getLink('PaidSubscriptionList', ['forceFrontend' => true]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getMessage() {
+ return $this->getLanguage()->getDynamicVariable('wcf.paidSubscription.notification.message', [
+ 'author' => $this->author,
+ 'notification' => $this->notification,
+ 'userNotificationObject' => $this->getUserNotificationObject()
+ ]);
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getTitle() {
+ return $this->getLanguage()->get('wcf.paidSubscription.notification.title');
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function isVisible() {
+ $userSubscriptionList = new PaidSubscriptionUserList();
+ $userSubscriptionList->getConditionBuilder()->add('userID = ?', [WCF::getUser()->userID]);
+ $userSubscriptionList->getConditionBuilder()->add('isActive = ?', [1]);
+
+ return $userSubscriptionList->countObjects() > 0;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object;
+use wcf\data\DatabaseObjectDecorator;
+use wcf\data\paid\subscription\user\PaidSubscriptionUser;
+use wcf\system\request\LinkHandler;
+
+/**
+ * Represents a paid subscription user as a notification object.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\User\Notification\Object
+ * @since 3.1
+ *
+ * @method PaidSubscriptionUser getDecoratedObject()
+ * @mixin PaidSubscriptionUser
+ */
+class PaidSubscriptionUserUserNotificationObject extends DatabaseObjectDecorator implements IUserNotificationObject {
+ /**
+ * @inheritDoc
+ */
+ protected static $baseClass = PaidSubscriptionUser::class;
+
+ /**
+ * @inheritDoc
+ */
+ public function getAuthorID() {
+ return null;
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getTitle() {
+ return $this->getSubscription()->getTitle();
+ }
+
+ /**
+ * @inheritDoc
+ */
+ public function getURL() {
+ return LinkHandler::getInstance()->getLink('PaidSubscriptionList', ['forceFrontend' => true]);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\user\notification\object\type;
+use wcf\data\paid\subscription\user\PaidSubscriptionUser;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+use wcf\system\user\notification\object\PaidSubscriptionUserUserNotificationObject;
+
+/**
+ * Represents a paid subscription user notification object type.
+ *
+ * @author Matthias Schmidt
+ * @copyright 2001-2017 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package WoltLabSuite\Core\System\User\Notification\Object\Type
+ * @since 3.1
+ */
+class PaidSubscriptionUserUserNotificationObjectType extends AbstractUserNotificationObjectType {
+ /**
+ * @inheritDoc
+ */
+ protected static $decoratorClassName = PaidSubscriptionUserUserNotificationObject::class;
+
+ /**
+ * @inheritDoc
+ */
+ protected static $objectClassName = PaidSubscriptionUser::class;
+
+ /**
+ * @inheritDoc
+ */
+ protected static $objectListClassName = PaidSubscriptionUserList::class;
+}
<item name="wcf.paidSubscription.returnMessage"><![CDATA[Danke für {if LANGUAGE_USE_INFORMAL_VARIANT}deine{else}Ihre{/if} Zahlung. {if LANGUAGE_USE_INFORMAL_VARIANT}Deine{else}Ihre{/if} Transaktion wurde abgeschlossen. Sobald {if LANGUAGE_USE_INFORMAL_VARIANT}deine{else}Ihre{/if} Zahlung von uns verarbeitet wurde, wird die erworbene Mitgliedschaft aktiviert.]]></item>
<item name="wcf.paidSubscription.confirmTOS"><![CDATA[Hiermit bestätige ich mein Einverständnis mit den <a href="{PAID_SUBSCRIPTION_TOS_URL}">Nutzungsbedingungen</a>]]></item>
<item name="wcf.paidSubscription.button.moreInformation"><![CDATA[Mehr Informationen]]></item>
+ <item name="wcf.paidSubscription.notification.title"><![CDATA[Ablaufende Mitgliedschaft]]></item>
+ <item name="wcf.paidSubscription.notification.message"><![CDATA[{if LANGUAGE_USE_INFORMAL_VARIANT}Deine{else}Ihre{/if} Mitgliedschaft „{$userNotificationObject->getTitle()}“ läuft {$userNotificationObject->endDate|dateDiff:$notification->time:false:true} (am {$userNotificationObject->endDate|date:'d. F'}) ab.]]></item>
</category>
<category name="wcf.payment">
<item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.notification.commentResponseOwner"><![CDATA[Neue Antwort auf einen Kommentar an {if LANGUAGE_USE_INFORMAL_VARIANT}deiner{else}Ihrer{/if} Pinnwand]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.like.notification.like"><![CDATA[Jemandem gefällt {if LANGUAGE_USE_INFORMAL_VARIANT}dein{else}Ihr{/if} Kommentar]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.like.notification.like"><![CDATA[Jemandem gefällt {if LANGUAGE_USE_INFORMAL_VARIANT}deine{else}Ihre{/if} Antwort auf einen Kommentar]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.paidSubscription.user.expiring"><![CDATA[Eine {if LANGUAGE_USE_INFORMAL_VARIANT}deiner{else}Ihrer{/if} Mitgliedschaft läuft bald ab]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.moderation"><![CDATA[Moderation]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.moderation.queue.notification.comment"><![CDATA[Neuer Kommentar in der Moderation]]></item>
<item name="wcf.paidSubscription.returnMessage"><![CDATA[Thank you for your payment, the transaction has been completed. Your subscription will be active once your payment has been processed by us.]]></item>
<item name="wcf.paidSubscription.confirmTOS"><![CDATA[I agree to the <a href="{PAID_SUBSCRIPTION_TOS_URL}">Terms of Service</a>]]></item>
<item name="wcf.paidSubscription.button.moreInformation"><![CDATA[More Details]]></item>
+ <item name="wcf.paidSubscription.notification.title"><![CDATA[Expiring Subscription]]></item>
+ <item name="wcf.paidSubscription.notification.message"><![CDATA[Your subscription “{$userNotificationObject->getTitle()}” will expire {$userNotificationObject->endDate|dateDiff:$notification->time:false:true} (on {$userNotificationObject->endDate|date:'F jS'}).]]></item>
</category>
<category name="wcf.payment">
<item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.notification.commentResponseOwner"><![CDATA[Notify me of new replies to comments on my wall]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.like.notification.like"><![CDATA[Notify me when my comments are liked]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.user.profileComment.response.like.notification.like"><![CDATA[Notify me when replies to my comments are liked]]></item>
+ <item name="wcf.user.notification.com.woltlab.wcf.paidSubscription.user.expiring"><![CDATA[Notify me before a subscription will expire]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.moderation"><![CDATA[Moderation]]></item>
<item name="wcf.user.notification.com.woltlab.wcf.moderation.queue.notification.comment"><![CDATA[Notify me when new comments are written in moderation]]></item>
startDate INT(10) NOT NULL DEFAULT 0,
endDate INT(10) NOT NULL DEFAULT 0,
isActive TINYINT(1) NOT NULL DEFAULT 1,
+ sentExpirationNotification TINYINT(1) NOT NULL DEFAULT 0,
UNIQUE KEY (subscriptionID, userID),
KEY (isActive)