<permissions>admin.notice.canManageNotice</permissions>
</acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.paidSubscription">
+ <parent>wcf.acp.menu.link.user</parent>
+ <showorder>6</showorder>
+ </acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.paidSubscription.list">
+ <controller><![CDATA[wcf\acp\page\PaidSubscriptionListPage]]></controller>
+ <parent>wcf.acp.menu.link.paidSubscription</parent>
+ <permissions>admin.paidSubscription.canManageSubscription</permissions>
+ <options>module_paid_subscription</options>
+ </acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.paidSubscription.user.list">
+ <controller><![CDATA[wcf\acp\page\PaidSubscriptionUserListPage]]></controller>
+ <parent>wcf.acp.menu.link.paidSubscription</parent>
+ <permissions>admin.paidSubscription.canManageSubscription</permissions>
+ <options>module_paid_subscription</options>
+ </acpmenuitem>
+ <acpmenuitem name="wcf.acp.menu.link.paidSubscription.transactionLog.list">
+ <controller><![CDATA[wcf\acp\page\PaidSubscriptionTransactionLogListPage]]></controller>
+ <parent>wcf.acp.menu.link.paidSubscription</parent>
+ <permissions>admin.paidSubscription.canManageSubscription</permissions>
+ <options>module_paid_subscription</options>
+ </acpmenuitem>
+
<acpmenuitem name="wcf.acp.menu.link.display">
<showorder>3</showorder>
</acpmenuitem>
<boxtype>content</boxtype>
</dashboardbox>
+ <dashboardbox name="com.woltlab.wcf.paidSubscriptions">
+ <classname><![CDATA[wcf\system\dashboard\box\PaidSubscriptionsDashboardBox]]></classname>
+ <boxtype>content</boxtype>
+ </dashboardbox>
+
<dashboardbox name="com.woltlab.wcf.user.recentActivitySidebar">
<classname><![CDATA[wcf\system\dashboard\box\RecentActivitySidebarDashboardBox]]></classname>
<boxtype>sidebar</boxtype>
<classname><![CDATA[wcf\system\dashboard\box\StaffOnlineSidebarDashboardBox]]></classname>
<boxtype>sidebar</boxtype>
</dashboardbox>
+
+ <dashboardbox name="com.woltlab.wcf.paidSubscriptionsSidebar">
+ <classname><![CDATA[wcf\system\dashboard\box\PaidSubscriptionsSidebarDashboardBox]]></classname>
+ <boxtype>sidebar</boxtype>
+ </dashboardbox>
</import>
</data>
<classname><![CDATA[wcf\system\message\embedded\object\AttachmentMessageEmbeddedObjectHandler]]></classname>
</type>
<!-- embedded object handlers -->
+
+ <type>
+ <name>com.woltlab.wcf.payment.method.paypal</name>
+ <definitionname>com.woltlab.wcf.payment.method</definitionname>
+ <classname><![CDATA[wcf\system\payment\method\PaypalPaymentMethod]]></classname>
+ </type>
+
+ <!-- <type>
+ <name>com.woltlab.wcf.payment.method.sofortUeberweisung</name>
+ <definitionname>com.woltlab.wcf.payment.method</definitionname>
+ <classname><![CDATA[wcf\system\payment\method\SofortUeberweisungPaymentMethod]]></classname>
+ </type>-->
+
+ <type>
+ <name>com.woltlab.wcf.payment.type.paidSubscription</name>
+ <definitionname>com.woltlab.wcf.payment.type</definitionname>
+ <classname><![CDATA[wcf\system\payment\type\PaidSubscriptionPaymentType]]></classname>
+ </type>
</import>
</data>
<definition>
<name>com.woltlab.wcf.message</name>
</definition>
+
+ <definition>
+ <name>com.woltlab.wcf.payment.method</name>
+ <interfacename><![CDATA[wcf\system\payment\method\IPaymentMethod]]></interfacename>
+ </definition>
+
+ <definition>
+ <name>com.woltlab.wcf.payment.type</name>
+ <interfacename><![CDATA[wcf\system\payment\type\IPaymentType]]></interfacename>
+ </definition>
</import>
</data>
<category name="general.cache.memcached">
<parent>general.cache</parent>
</category>
+ <category name="general.payment">
+ <parent>general</parent>
+ </category>
<!-- /general -->
<!-- user -->
</option>
<!-- /general.cache -->
+ <!-- general.payment -->
+ <option name="available_payment_methods">
+ <categoryname>general.payment</categoryname>
+ <optiontype>paymentMethodSelect</optiontype>
+ </option>
+
+ <option name="paypal_email_address">
+ <categoryname>general.payment</categoryname>
+ <optiontype>text</optiontype>
+ </option>
+
+ <option name="module_paid_subscription">
+ <categoryname>general.payment</categoryname>
+ <optiontype>boolean</optiontype>
+ </option>
+ <!-- /general.payment -->
+
<option name="attachment_storage">
<categoryname>message.attachment</categoryname>
<optiontype>text</optiontype>
--- /dev/null
+<header class="boxHeadline boxSubHeadline">
+ <h2>{lang}wcf.dashboard.box.com.woltlab.wcf.paidSubscriptions{/lang}</h2>
+</header>
+
+<div class="container marginTop containerPadding">
+ <ul class="paidSubscriptionTeaserList">
+ {foreach from=$subscriptions item=subscription}
+ <li>
+ <div class="containerHeadline" title="{$subscription->description|language}">
+ <h3>{$subscription->title|language}</h3>
+ <small>{lang}wcf.paidSubscription.formattedCost{/lang}</small>
+ </div>
+
+ <ul class="buttonList marginTopTiny">
+ {foreach from=$subscription->getPurchaseButtons() item=button}
+ <li>{@$button}</li>
+ {/foreach}
+ </ul>
+ </li>
+ {/foreach}
+ </ul>
+</div>
--- /dev/null
+<ul class="sidebarBoxList">
+ {foreach from=$subscriptions item=subscription}
+ <li>
+ <div class="sidebarBoxHeadline" title="{$subscription->description|language}">
+ <h3>{$subscription->title|language}</h3>
+ <small>{lang}wcf.paidSubscription.formattedCost{/lang}</small>
+ </div>
+
+ <ul class="buttonList marginTopTiny">
+ {foreach from=$subscription->getPurchaseButtons() item=button}
+ <li>{@$button}</li>
+ {/foreach}
+ </ul>
+ </li>
+ {/foreach}
+</ul>
--- /dev/null
+{include file='documentHeader'}
+
+<head>
+ <title>{lang}wcf.user.menu.settings.paidSubscription{/lang} - {lang}wcf.user.menu.settings{/lang} - {PAGE_TITLE|language}</title>
+
+ {include file='headInclude'}
+</head>
+
+<body id="tpl{$templateName|ucfirst}" data-template="{$templateName}" data-application="{$templateNameApplication}">
+
+{include file='userMenuSidebar'}
+
+{include file='header' sidebarOrientation='left'}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.user.menu.settings.paidSubscription{/lang}</h1>
+</header>
+
+{include file='userNotice'}
+
+<div class="contentNavigation">
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsTop'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+{if $subscriptions|count}
+ <header class="boxHeadline boxSubHeadline">
+ <h2>{lang}wcf.paidSubscription.availableSubscriptions{/lang}</h2>
+ </header>
+
+ <div class="container marginTop">
+ <ul class="containerList">
+ {foreach from=$subscriptions item=subscription}
+ <li>
+ <div class="containerHeadline">
+ <h3>{$subscription->title|language}</h3>
+ <p>{$subscription->description|language}</p>
+
+ <p class="marginTopTiny">{lang}wcf.paidSubscription.formattedCost{/lang}</p>
+
+ <ul class="buttonList marginTopTiny">
+ {foreach from=$subscription->getPurchaseButtons() item=button}
+ <li>{@$button}</li>
+ {/foreach}
+ </ul>
+ </div>
+ </li>
+ {/foreach}
+ </ul>
+ </div>
+{/if}
+
+{if $userSubscriptions|count}
+ <header class="boxHeadline boxSubHeadline">
+ <h2>{lang}wcf.paidSubscription.purchasedSubscriptions{/lang}</h2>
+ </header>
+
+ <div class="container marginTop">
+ <ul class="containerList">
+ {foreach from=$userSubscriptions item=userSubscription}
+ <li>
+ <div class="containerHeadline">
+ <h3>{$userSubscription->getSubscription()->title|language}</h3>
+ <p>{$userSubscription->getSubscription()->description|language}</p>
+
+ {if $userSubscription->endDate}
+ <p>{lang}wcf.paidSubscription.expires{/lang}: {@$userSubscription->endDate|time}</p>
+ {/if}
+ </div>
+ </li>
+ {/foreach}
+ </ul>
+ </div>
+{/if}
+
+{if !$subscriptions|count && !$userSubscriptions|count}
+ <p class="warning">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsBottom'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+{include file='footer'}
+
+</body>
+</html>
<usersonly>1</usersonly>
</option>
+ <option name="admin.paidSubscription.canManageSubscription">
+ <categoryname>admin.user.user</categoryname>
+ <optiontype>boolean</optiontype>
+ <defaultvalue>0</defaultvalue>
+ <admindefaultvalue>1</admindefaultvalue>
+ <usersonly>1</usersonly>
+ <options>module_paid_subscription</options>
+ </option>
+
<option name="admin.content.dashboard.canEditDashboard">
<categoryname>admin.display</categoryname>
<optiontype>boolean</optiontype>
<controller><![CDATA[wcf\form\NotificationSettingsForm]]></controller>
<parent>wcf.user.menu.settings</parent>
</usermenuitem>
+
+ <usermenuitem name="wcf.user.menu.settings.paidSubscription">
+ <controller><![CDATA[wcf\page\PaidSubscriptionListPage]]></controller>
+ <parent>wcf.user.menu.settings</parent>
+ <options>module_paid_subscription</options>
+ </usermenuitem>
<!-- /settings -->
<!-- community -->
--- /dev/null
+{include file='header' pageTitle='wcf.acp.paidSubscription.'|concat:$action}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ $('#subscriptionLengthPermanent').change(function() {
+ if ($('#subscriptionLengthPermanent').is(':checked')) {
+ $('#subscriptionLengthDL, #isRecurringDL').hide();
+ }
+ else {
+ $('#subscriptionLengthDL, #isRecurringDL').show();
+ }
+ });
+ $('#subscriptionLengthPermanent').change();
+ });
+ //]]>
+</script>
+
+{include file='multipleLanguageInputJavascript' elementIdentifier='description' forceSelection=false}
+{include file='multipleLanguageInputJavascript' elementIdentifier='title' forceSelection=false}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.paidSubscription.{$action}{/lang}</h1>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.{$action}{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='PaidSubscriptionList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.paidSubscription.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{if $action == 'add'}{link controller='PaidSubscriptionAdd'}{/link}{else}{link controller='PaidSubscriptionEdit' id=$subscriptionID}{/link}{/if}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'title'} class="formError"{/if}>
+ <dt><label for="title">{lang}wcf.global.title{/lang}</label></dt>
+ <dd>
+ <input type="text" id="title" name="title" value="{$i18nPlainValues['title']}" autofocus="autofocus" class="medium" />
+ {if $errorField == 'title'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {elseif $errorType == 'multilingual'}
+ {lang}wcf.global.form.error.multilingual{/lang}
+ {else}
+ {lang}wcf.acp.paidSubscription.title.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'description'} class="formError"{/if}>
+ <dt><label for="description">{lang}wcf.global.description{/lang}</label></dt>
+ <dd>
+ <textarea id="description" name="description" cols="40" rows="10">{$i18nPlainValues[description]}</textarea>
+ {if $errorField == 'description'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.paidSubscription.description.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="showOrder">{lang}wcf.acp.paidSubscription.showOrder{/lang}</label></dt>
+ <dd>
+ <input type="number" id="showOrder" name="showOrder" value="{if $showOrder}{@$showOrder}{/if}" class="tiny" min="0" />
+ <small>{lang}wcf.acp.paidSubscription.showOrder.description{/lang}</small>
+ </dd>
+ </dl>
+
+ <dl>
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" name="isDisabled" value="1" {if $isDisabled}checked="checked" {/if}/> {lang}wcf.acp.paidSubscription.isDisabled{/lang}</label>
+ <small>{lang}wcf.acp.paidSubscription.isDisabled.description{/lang}</small>
+ </dd>
+ </dl>
+
+ {if $availableSubscriptions|count}
+ <dl>
+ <dt>{lang}wcf.acp.paidSubscription.excludedSubscriptions{/lang}</dt>
+ <dd>
+ {foreach from=$availableSubscriptions item=availableSubscription}
+ <label><input type="checkbox" name="excludedSubscriptionIDs[]" value="{@$availableSubscription->subscriptionID}" {if $availableSubscription->subscriptionID|in_array:$excludedSubscriptionIDs}checked="checked" {/if}/> {$availableSubscription->title|language}</label>
+ {/foreach}
+ <small>{lang}wcf.acp.paidSubscription.excludedSubscriptions.description{/lang}</small>
+ </dd>
+ </dl>
+ {/if}
+
+ {event name='dataFields'}
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.paidSubscription.paymentOptions{/lang}</legend>
+
+ <dl{if $errorField == 'cost'} class="formError"{/if}>
+ <dt><label for="cost">{lang}wcf.acp.paidSubscription.cost{/lang}</label></dt>
+ <dd>
+ <input type="number" id="cost" name="cost" value="{$cost}" class="tiny" step="0.01" min="0" />
+ <select name="currency" id="currency">
+ {htmlOptions values=$availableCurrencies output=$availableCurrencies selected=$currency}
+ </select>
+ {if $errorField == 'cost'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.paidSubscription.cost.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl>
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" id="subscriptionLengthPermanent" name="subscriptionLengthPermanent" value="1" {if !$subscriptionLength}checked="checked" {/if}/> {lang}wcf.acp.paidSubscription.subscriptionLength.permanent{/lang}</label>
+ </dd>
+ </dl>
+
+ <dl id="subscriptionLengthDL"{if $errorField == 'subscriptionLength'} class="formError"{/if}>
+ <dt><label for="subscriptionLength">{lang}wcf.acp.paidSubscription.subscriptionLength{/lang}</label></dt>
+ <dd>
+ <input type="number" id="subscriptionLength" name="subscriptionLength" value="{@$subscriptionLength}" class="tiny" />
+ <select name="subscriptionLengthUnit" id="subscriptionLengthUnit">
+ <option value="D"{if $subscriptionLengthUnit == 'D'} selected="selected"{/if}>{lang}wcf.acp.paidSubscription.subscriptionLengthUnit.D{/lang}</option>
+ <option value="M"{if $subscriptionLengthUnit == 'M'} selected="selected"{/if}>{lang}wcf.acp.paidSubscription.subscriptionLengthUnit.M{/lang}</option>
+ <option value="Y"{if $subscriptionLengthUnit == 'Y'} selected="selected"{/if}>{lang}wcf.acp.paidSubscription.subscriptionLengthUnit.Y{/lang}</option>
+ </select>
+ {if $errorField == 'subscriptionLength'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.paidSubscription.subscriptionLength.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ <dl id="isRecurringDL">
+ <dt></dt>
+ <dd>
+ <label><input type="checkbox" name="isRecurring" value="1" {if $isRecurring}checked="checked" {/if}/> {lang}wcf.acp.paidSubscription.isRecurring{/lang}</label>
+ <small>{lang}wcf.acp.paidSubscription.isRecurring.description{/lang}</small>
+ </dd>
+ </dl>
+
+ <dl{if $errorField == 'groupIDs'} class="formError"{/if}>
+ <dt><label>{lang}wcf.acp.paidSubscription.userGroups{/lang}</label></dt>
+ <dd>
+ {foreach from=$availableUserGroups item=userGroup}
+ <label><input type="checkbox" name="groupIDs[]" value="{@$userGroup->groupID}" {if $userGroup->groupID|in_array:$groupIDs}checked="checked" {/if}/> {$userGroup->groupName|language}</label>
+ {/foreach}
+ {if $errorField == 'groupIDs'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.paidSubscription.userGroups.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ <small>{lang}wcf.acp.paidSubscription.userGroups.description{/lang}</small>
+
+ </dd>
+ </dl>
+ </fieldset>
+
+ {event name='fieldsets'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+{include file='footer'}
\ No newline at end of file
--- /dev/null
+{include file='header' pageTitle='wcf.acp.paidSubscription.list'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\paid\\subscription\\PaidSubscriptionAction', '.jsPaidSubscriptionRow');
+ new WCF.Action.Toggle('wcf\\data\\paid\\subscription\\PaidSubscriptionAction', '.jsPaidSubscriptionRow');
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.paidSubscription.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller='PaidSubscriptionList' link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='PaidSubscriptionAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.paidSubscription.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.paidSubscription.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnSubscriptionID{if $sortField == 'subscriptionID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='PaidSubscriptionList'}pageNo={@$pageNo}&sortField=subscriptionID&sortOrder={if $sortField == 'subscriptionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnTitle{if $sortField == 'title'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionList'}pageNo={@$pageNo}&sortField=title&sortOrder={if $sortField == 'title' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.title{/lang}</a></th>
+ <th class="columnDigits columnCost{if $sortField == 'cost'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionList'}pageNo={@$pageNo}&sortField=cost&sortOrder={if $sortField == 'cost' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.paidSubscription.cost{/lang}</a></th>
+ <th class="columnDigits columnSubscriptionLength{if $sortField == 'subscriptionLength'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionList'}pageNo={@$pageNo}&sortField=subscriptionLength&sortOrder={if $sortField == 'subscriptionLength' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.paidSubscription.subscriptionLength{/lang}</a></th>
+ <th class="columnDigits columnShowOrder{if $sortField == 'showOrder'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionList'}pageNo={@$pageNo}&sortField=showOrder&sortOrder={if $sortField == 'showOrder' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.paidSubscription.showOrder{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item=subscription}
+ <tr class="jsPaidSubscriptionRow">
+ <td class="columnIcon">
+ <span class="icon icon16 icon-check{if $subscription->isDisabled}-empty{/if} jsToggleButton jsTooltip pointer" title="{lang}wcf.global.button.{if !$subscription->isDisabled}disable{else}enable{/if}{/lang}" data-object-id="{@$subscription->subscriptionID}"></span>
+ <a href="{link controller='PaidSubscriptionEdit' id=$subscription->subscriptionID}{/link}" title="{lang}wcf.global.button.edit{/lang}" class="jsTooltip"><span class="icon icon16 icon-pencil"></span></a>
+ <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$subscription->subscriptionID}" data-confirm-message="{lang}wcf.acp.paidSubscription.delete.confirmMessage{/lang}"></span>
+ <a href="{link controller='PaidSubscriptionUserAdd' id=$subscription->subscriptionID}{/link}" title="{lang}wcf.acp.paidSubscription.user.add{/lang}" class="jsTooltip"><span class="icon icon16 icon-plus"></span></a>
+
+ {event name='itemButtons'}
+ </td>
+ <td class="columnID columnSubscriptionID">{@$subscription->subscriptionID}</td>
+ <td class="columnTitle"><a href="{link controller='PaidSubscriptionEdit' id=$subscription->subscriptionID}{/link}" title="{lang}wcf.acp.paidSubscription.edit{/lang}">{$subscription->title|language}</a></td>
+ <td class="columnDigits columnCost">{@$subscription->currency} {$subscription->cost|currency}</td>
+ <td class="columnDigits columnSubscriptionLength">{if $subscription->subscriptionLength}{@$subscription->subscriptionLength} {lang}wcf.acp.paidSubscription.subscriptionLengthUnit.{@$subscription->subscriptionLengthUnit}{/lang}{else}∞{/if}</td>
+ <td class="columnDigits columnShowOrder">{@$subscription->showOrder}</td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+ <nav>
+ <ul>
+ <li><a href="{link controller='PaidSubscriptionAdd'}{/link}" class="button"><span class="icon icon16 icon-plus"></span> <span>{lang}wcf.acp.paidSubscription.add{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+ </div>
+{else}
+ <p class="warning">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+{include file='footer'}
--- /dev/null
+{capture assign='pageTitle'}{lang}wcf.acp.paidSubscription.transactionLog{/lang}: {@$log->logID}{/capture}
+{include file='header'}
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.paidSubscription.transactionLog{/lang}: {@$log->logID}</h1>
+</header>
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='PaidSubscriptionTransactionLogList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.paidSubscription.transactionLog.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsTop'}
+ </ul>
+ </nav>
+</div>
+
+<div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.acp.paidSubscription.transactionLog{/lang}: {@$log->logID}</legend>
+
+ <dl>
+ <dt>{lang}wcf.acp.paidSubscription.transactionLog.logMessage{/lang}</dt>
+ <dd>{$log->logMessage}</dd>
+
+ {if $log->userID}
+ <dt>{lang}wcf.user.username{/lang}</dt>
+ <dd><a href="{link controller='UserEdit' id=$log->userID}{/link}" title="{lang}wcf.acp.user.edit{/lang}">{$log->getUser()->username}</a></dd>
+ {/if}
+
+ {if $log->subscriptionID}
+ <dt>{lang}wcf.acp.paidSubscription.subscription{/lang}</dt>
+ <dd>{$log->getSubscription()->title|language}</dd>
+ {/if}
+
+ <dt>{lang}wcf.acp.paidSubscription.transactionLog.paymentMethod{/lang}</dt>
+ <dd>{lang}wcf.payment.{@$log->getPaymentMethodName()}{/lang}</dd>
+
+ <dt>{lang}wcf.acp.paidSubscription.transactionLog.transactionID{/lang}</dt>
+ <dd>{$log->transactionID}</dd>
+
+ <dt>{lang}wcf.acp.paidSubscription.transactionLog.logTime{/lang}</dt>
+ <dd>{@$log->logTime|time}</dd>
+ </dl>
+ </fieldset>
+
+ <fieldset>
+ <legend>{lang}wcf.acp.paidSubscription.transactionLog.transactionDetails{/lang}</legend>
+
+ <dl>
+ {foreach from=$log->getTransactionDetails() key=key item=value}
+ <dt>{$key}</dt>
+ <dd>{$value}</dd>
+ {/foreach}
+ </dl>
+ </fieldset>
+</div>
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='PaidSubscriptionTransactionLogList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.paidSubscription.transactionLog.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtonsBottom'}
+ </ul>
+ </nav>
+</div>
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.paidSubscription.transactionLog.list'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ WCF.TabMenu.init();
+ new WCF.Search.User('#username');
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.paidSubscription.transactionLog.list{/lang}</h1>
+</header>
+
+<form method="post" action="{link controller='PaidSubscriptionTransactionLogList'}{/link}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.filter{/lang}</legend>
+
+ <dl>
+ <dt><label for="transactionID">{lang}wcf.acp.paidSubscription.transactionLog.transactionID{/lang}</label></dt>
+ <dd>
+ <input type="text" id="transactionID" name="transactionID" value="{$transactionID}" class="long" />
+ </dd>
+ </dl>
+
+ <dl>
+ <dt><label for="username">{lang}wcf.user.username{/lang}</label></dt>
+ <dd>
+ <input type="text" id="username" name="username" value="{$username}" class="long" />
+ </dd>
+ </dl>
+
+ {if $availableSubscriptions|count}
+ <dl>
+ <dt><label for="subscriptionID">{lang}wcf.acp.paidSubscription.subscription{/lang}</label></dt>
+ <dd>
+ <select name="subscriptionID" id="subscriptionID">
+ <option value="0">{lang}wcf.global.noSelection{/lang}</option>
+ {foreach from=$availableSubscriptions item=availableSubscription}
+ <option value="{@$availableSubscription->subscriptionID}"{if $availableSubscription->subscriptionID == $subscriptionID} selected="selected"{/if}>{$availableSubscription->title|language}</option>
+ {/foreach}
+ </select>
+ </dd>
+ </dl>
+ {/if}
+ </fieldset>
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+<div class="contentNavigation">
+ {assign var='linkParameters' value=''}
+ {if $transactionID}{capture append=linkParameters}&transactionID={@$transactionID|rawurlencode}{/capture}{/if}
+ {if $username}{capture append=linkParameters}&username={@$username|rawurlencode}{/capture}{/if}
+ {if $subscriptionID}{capture append=linkParameters}&subscriptionID={@$subscriptionID}{/capture}{/if}
+
+ {pages print=true assign=pagesLinks controller='PaidSubscriptionTransactionLogList' link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder$linkParameters"}
+
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsTop'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.paidSubscription.transactionLog.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnLogID{if $sortField == 'logID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=logID&sortOrder={if $sortField == 'logID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnTitle columnLogMessage{if $sortField == 'logMessage'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=logMessage&sortOrder={if $sortField == 'logMessage' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.paidSubscription.transactionLog.logMessage{/lang}</a></th>
+ <th class="columnText columnUsername{if $sortField == 'userID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=userID&sortOrder={if $sortField == 'userID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.user.username{/lang}</a></th>
+ <th class="columnText columnSubscriptionTitle{if $sortField == 'subscriptionID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=subscriptionID&sortOrder={if $sortField == 'subscriptionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.paidSubscription.subscription{/lang}</a></th>
+ <th class="columnText columnPaymentMethod{if $sortField == 'paymentMethodObjectTypeID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=paymentMethodObjectTypeID&sortOrder={if $sortField == 'paymentMethodObjectTypeID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.paidSubscription.transactionLog.paymentMethod{/lang}</a></th>
+ <th class="columnText columnTransactionID{if $sortField == 'transactionID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=transactionID&sortOrder={if $sortField == 'transactionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.paidSubscription.transactionLog.transactionID{/lang}</a></th>
+ <th class="columnDate columnLogTime{if $sortField == 'logTime'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionTransactionLogList'}pageNo={@$pageNo}&sortField=logTime&sortOrder={if $sortField == 'logTime' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{@$linkParameters}{/link}">{lang}wcf.acp.paidSubscription.transactionLog.logTime{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item=log}
+ <tr>
+ <td class="columnID columnLogID">{@$log->logID}</td>
+ <td class="columnTitle columnLogMessage"><a href="{link controller='PaidSubscriptionTransactionLog' id=$log->logID}{/link}">{$log->logMessage}</a></td>
+ <td class="columnText columnUsername"><a href="{link controller='UserEdit' id=$log->userID}{/link}" title="{lang}wcf.acp.user.edit{/lang}">{$log->username}</a></td>
+ <td class="columnText columnSubscriptionTitle">{$log->title|language}</td>
+ <td class="columnText columnPaymentMethod">{lang}wcf.payment.{@$log->getPaymentMethodName()}{/lang}</td>
+ <td class="columnText columnTransactionID">{$log->transactionID}</td>
+ <td class="columnDate columnLogTime">{@$log->logTime|time}</td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsBottom'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+ </div>
+{else}
+ <p class="warning">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.paidSubscription.user.add'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ WCF.TabMenu.init();
+ new WCF.Search.User('#username');
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.paidSubscription.user.add{/lang}</h1>
+ <p>{$subscription->title|language}</p>
+</header>
+
+{include file='formError'}
+
+{if $success|isset}
+ <p class="success">{lang}wcf.global.success.add{/lang}</p>
+{/if}
+
+<div class="contentNavigation">
+ <nav>
+ <ul>
+ <li><a href="{link controller='PaidSubscriptionList'}{/link}" class="button"><span class="icon icon16 icon-list"></span> <span>{lang}wcf.acp.menu.link.paidSubscription.list{/lang}</span></a></li>
+
+ {event name='contentNavigationButtons'}
+ </ul>
+ </nav>
+</div>
+
+<form method="post" action="{link controller='PaidSubscriptionUserAdd' id=$subscriptionID}{/link}">
+ <div class="container containerPadding marginTop">
+ <fieldset>
+ <legend>{lang}wcf.global.form.data{/lang}</legend>
+
+ <dl{if $errorField == 'username'} class="formError"{/if}>
+ <dt><label for="username">{lang}wcf.user.username{/lang}</label></dt>
+ <dd>
+ <input type="text" id="username" name="username" value="{$username}" autofocus="autofocus" class="medium" />
+ {if $errorField == 'username'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.user.username.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+
+ {if $subscription->subscriptionLength}
+ <dl{if $errorField == 'endDate'} class="formError"{/if}>
+ <dt><label for="endDate">{lang}wcf.acp.paidSubscription.user.endDate{/lang}</label></dt>
+ <dd>
+ <input type="date" id="endDate" name="endDate" value="{$endDate}" class="medium" data-ignore-timezone="true" />
+ {if $errorField == 'endDate'}
+ <small class="innerError">
+ {if $errorType == 'empty'}
+ {lang}wcf.global.form.error.empty{/lang}
+ {else}
+ {lang}wcf.acp.paidSubscription.user.endDate.error.{@$errorType}{/lang}
+ {/if}
+ </small>
+ {/if}
+ </dd>
+ </dl>
+ {/if}
+ </fieldset>
+
+ {event name='fieldsets'}
+ </div>
+
+ <div class="formSubmit">
+ <input type="submit" value="{lang}wcf.global.button.submit{/lang}" accesskey="s" />
+ {@SECURITY_TOKEN_INPUT_TAG}
+ </div>
+</form>
+
+{include file='footer'}
--- /dev/null
+{include file='header' pageTitle='wcf.acp.paidSubscription.user.list'}
+
+<script data-relocate="true">
+ //<![CDATA[
+ $(function() {
+ new WCF.Action.Delete('wcf\\data\\paid\\subscription\\user\\PaidSubscriptionUserAction', '.jsPaidSubscriptionUserRow');
+ });
+ //]]>
+</script>
+
+<header class="boxHeadline">
+ <h1>{lang}wcf.acp.paidSubscription.user.list{/lang}</h1>
+</header>
+
+<div class="contentNavigation">
+ {pages print=true assign=pagesLinks controller='PaidSubscriptionUserList' link="pageNo=%d&sortField=$sortField&sortOrder=$sortOrder"}
+
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsTop'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+</div>
+
+{if $objects|count}
+ <div class="tabularBox tabularBoxTitle marginTop">
+ <header>
+ <h2>{lang}wcf.acp.paidSubscription.user.list{/lang} <span class="badge badgeInverse">{#$items}</span></h2>
+ </header>
+
+ <table class="table">
+ <thead>
+ <tr>
+ <th class="columnID columnSubscriptionUserID{if $sortField == 'subscriptionUserID'} active {@$sortOrder}{/if}" colspan="2"><a href="{link controller='PaidSubscriptionUserList'}pageNo={@$pageNo}&sortField=subscriptionUserID&sortOrder={if $sortField == 'subscriptionUserID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.global.objectID{/lang}</a></th>
+ <th class="columnText columnUsername{if $sortField == 'userID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionUserList'}pageNo={@$pageNo}&sortField=userID&sortOrder={if $sortField == 'userID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.user.username{/lang}</a></th>
+ <th class="columnText columnSubscriptionTitle{if $sortField == 'subscriptionID'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionUserList'}pageNo={@$pageNo}&sortField=subscriptionID&sortOrder={if $sortField == 'subscriptionID' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.paidSubscription.subscription{/lang}</a></th>
+ <th class="columnDate columnEndDate{if $sortField == 'endDate'} active {@$sortOrder}{/if}"><a href="{link controller='PaidSubscriptionUserList'}pageNo={@$pageNo}&sortField=endDate&sortOrder={if $sortField == 'endDate' && $sortOrder == 'ASC'}DESC{else}ASC{/if}{/link}">{lang}wcf.acp.paidSubscription.user.endDate{/lang}</a></th>
+
+ {event name='columnHeads'}
+ </tr>
+ </thead>
+
+ <tbody>
+ {foreach from=$objects item=subscriptionUser}
+ <tr class="jsPaidSubscriptionUserRow">
+ <td class="columnIcon">
+ <span class="icon icon16 icon-remove jsDeleteButton jsTooltip pointer" title="{lang}wcf.global.button.delete{/lang}" data-object-id="{@$subscriptionUser->subscriptionUserID}" data-confirm-message="{lang}wcf.acp.paidSubscription.user.delete.confirmMessage{/lang}"></span>
+
+ {event name='itemButtons'}
+ </td>
+ <td class="columnID columnSubscriptionUserID">{@$subscriptionUser->subscriptionUserID}</td>
+ <td class="columnText columnUsername"><a href="{link controller='UserEdit' id=$subscriptionUser->userID}{/link}" title="{lang}wcf.acp.user.edit{/lang}">{$subscriptionUser->username}</a></td>
+ <td class="columnText columnSubscriptionTitle">{$subscriptionUser->title|language}</td>
+ <td class="columnDate columnEndDate">{if $subscriptionUser->endDate}{@$subscriptionUser->endDate|time}{/if}</td>
+
+ {event name='columns'}
+ </tr>
+ {/foreach}
+ </tbody>
+ </table>
+
+ </div>
+
+ <div class="contentNavigation">
+ {@$pagesLinks}
+
+ {hascontent}
+ <nav>
+ <ul>
+ {content}
+ {event name='contentNavigationButtonsBottom'}
+ {/content}
+ </ul>
+ </nav>
+ {/hascontent}
+ </div>
+{else}
+ <p class="warning">{lang}wcf.global.noItems{/lang}</p>
+{/if}
+
+{include file='footer'}
--- /dev/null
+{foreach from=$selectOptions key=key item=selectOption}
+ <label><input type="checkbox" name="values[{$option->optionName}][]" value="{$key}"{if $key|in_array:$value} checked="checked"{/if} /> {lang}{@$selectOption}{/lang}</label>
+{/foreach}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\paid\subscription\PaidSubscriptionAction;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\data\paid\subscription\PaidSubscriptionEditor;
+use wcf\data\user\group\UserGroup;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\language\I18nHandler;
+use wcf\system\payment\method\PaymentMethodHandler;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+use wcf\data\paid\subscription\PaidSubscriptionList;
+
+/**
+ * Shows the paid subscription add form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class PaidSubscriptionAddForm extends AbstractForm {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.paidSubscription';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'paidSubscriptionAdd';
+
+ /**
+ * subscription title
+ * @var string
+ */
+ public $title = '';
+
+ /**
+ * subscription description
+ * @var string
+ */
+ public $description = '';
+
+ /**
+ * indicates if the subscription is disabled
+ * @var boolean
+ */
+ public $isDisabled = 0;
+
+ /**
+ * subscription show order
+ * @var integer
+ */
+ public $showOrder = 0;
+
+ /**
+ * subscription cost
+ * @var double
+ */
+ public $cost = 0.0;
+
+ /**
+ * subscription currency
+ * @var string
+ */
+ public $currency = 'USD';
+
+ /**
+ * indicates if the subscription is permanent
+ * @var boolean
+ */
+ public $subscriptionLengthPermanent = 0;
+
+ /**
+ * subscription length
+ * @var integer
+ */
+ public $subscriptionLength = 0;
+
+ /**
+ * subscription length unit
+ * @var string
+ */
+ public $subscriptionLengthUnit = '';
+
+ /**
+ * indicates if the subscription is a recurring payment
+ * @var boolean
+ */
+ public $isRecurring = 0;
+
+ /**
+ * list of group ids
+ * @var array<intewer>
+ */
+ public $groupIDs = array();
+
+ /**
+ * list of excluded subscriptions
+ * @var array<integer>
+ */
+ public $excludedSubscriptionIDs = array();
+
+ /**
+ * available user groups
+ * @var array
+ */
+ public $availableUserGroups = array();
+
+ /**
+ * list of available currencies
+ * @var array<string>
+ */
+ public $availableCurrencies = array();
+
+ /**
+ * list of available subscriptions
+ * @var array
+ */
+ public $availableSubscriptions = array();
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ I18nHandler::getInstance()->register('description');
+ I18nHandler::getInstance()->register('title');
+
+ // get available user groups
+ $this->availableUserGroups = UserGroup::getAccessibleGroups(array(), array(UserGroup::GUESTS, UserGroup::EVERYONE, UserGroup::USERS));
+
+ // get available currencies
+ foreach (PaymentMethodHandler::getInstance()->getPaymentMethods() as $paymentMethod) {
+ $this->availableCurrencies = array_merge($this->availableCurrencies, $paymentMethod->getSupportedCurrencies());
+ }
+ $this->availableCurrencies = array_unique($this->availableCurrencies);
+ sort($this->availableCurrencies);
+
+ // get available subscriptions
+ $this->getAvailableSubscriptions();
+ }
+
+ protected function getAvailableSubscriptions() {
+ $subscriptionList = new PaidSubscriptionList();
+ $subscriptionList->sqlOrderBy = 'title';
+ $subscriptionList->readObjects();
+ $this->availableSubscriptions = $subscriptionList->getObjects();
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ // read i18n values
+ I18nHandler::getInstance()->readValues();
+
+ // handle i18n plain input
+ if (I18nHandler::getInstance()->isPlainValue('description')) $this->description = I18nHandler::getInstance()->getValue('description');
+ if (I18nHandler::getInstance()->isPlainValue('title')) $this->title = I18nHandler::getInstance()->getValue('title');
+
+ if (!empty($_POST['isDisabled'])) $this->isDisabled = 1;
+ if (isset($_POST['showOrder'])) $this->showOrder = intval($_POST['showOrder']);
+ if (isset($_POST['cost'])) $this->cost = floatval($_POST['cost']);
+ if (isset($_POST['currency'])) $this->currency = $_POST['currency'];
+ if (!empty($_POST['subscriptionLengthPermanent'])) $this->subscriptionLengthPermanent = 1;
+ if (isset($_POST['subscriptionLength'])) $this->subscriptionLength = intval($_POST['subscriptionLength']);
+ if (isset($_POST['subscriptionLengthUnit'])) $this->subscriptionLengthUnit = $_POST['subscriptionLengthUnit'];
+ if (!empty($_POST['isRecurring'])) $this->isRecurring = 1;
+ if (isset($_POST['groupIDs']) && is_array($_POST['groupIDs'])) $this->groupIDs = ArrayUtil::toIntegerArray($_POST['groupIDs']);
+ if (isset($_POST['excludedSubscriptionIDs']) && is_array($_POST['excludedSubscriptionIDs'])) $this->excludedSubscriptionIDs = ArrayUtil::toIntegerArray($_POST['excludedSubscriptionIDs']);
+ }
+
+ /**
+ * @see \wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ // validate title
+ if (!I18nHandler::getInstance()->validateValue('title')) {
+ if (I18nHandler::getInstance()->isPlainValue('title')) {
+ throw new UserInputException('title');
+ }
+ else {
+ throw new UserInputException('title', 'multilingual');
+ }
+ }
+
+ // validate description
+ if (!I18nHandler::getInstance()->validateValue('description', false, true)) {
+ throw new UserInputException('description');
+ }
+
+ // validate cost
+ if ($this->cost < 0.01) {
+ throw new UserInputException('cost');
+ }
+ // validate currency
+ if (!in_array($this->currency, $this->availableCurrencies)) {
+ throw new UserInputException('cost');
+ }
+
+ if (!$this->subscriptionLengthPermanent) {
+ if ($this->subscriptionLength < 1) {
+ throw new UserInputException('subscriptionLength');
+ }
+ if ($this->subscriptionLengthUnit != 'D' && $this->subscriptionLengthUnit != 'M' && $this->subscriptionLengthUnit != 'Y') {
+ throw new UserInputException('subscriptionLength');
+ }
+ if (($this->subscriptionLengthUnit == 'D' && $this->subscriptionLength > 90) || ($this->subscriptionLengthUnit == 'M' && $this->subscriptionLength > 24) || ($this->subscriptionLengthUnit == 'Y' && $this->subscriptionLength > 5)) {
+ throw new UserInputException('subscriptionLength', 'invalid');
+ }
+ }
+
+ // validate group ids
+ if (empty($this->groupIDs)) {
+ throw new UserInputException('groupIDs');
+ }
+ foreach ($this->groupIDs as $groupID) {
+ if (!isset($this->availableUserGroups[$groupID])) throw new UserInputException('groupIDs');
+ }
+ // validate excluded subscriptions
+ foreach ($this->excludedSubscriptionIDs as $key => $subscriptionID) {
+ if (!isset($this->availableSubscriptions[$subscriptionID])) unset($this->excludedSubscriptionIDs[$key]);
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ // save subscription
+ $this->objectAction = new PaidSubscriptionAction(array(), 'create', array('data' => array_merge($this->additionalFields, array(
+ 'title' => $this->title,
+ 'description' => $this->description,
+ 'isDisabled' => $this->isDisabled,
+ 'showOrder' => $this->showOrder,
+ 'cost' => $this->cost,
+ 'currency' => $this->currency,
+ 'subscriptionLength' => $this->subscriptionLength,
+ 'subscriptionLengthUnit' => $this->subscriptionLengthUnit,
+ 'isRecurring' => $this->isRecurring,
+ 'groupIDs' => implode(',', $this->groupIDs),
+ 'excludedSubscriptionIDs' => implode(',', $this->excludedSubscriptionIDs)
+ ))));
+ $returnValues = $this->objectAction->executeAction();
+
+ // save i18n values
+ $this->saveI18nValue($returnValues['returnValues'], 'description');
+ $this->saveI18nValue($returnValues['returnValues'], 'title');
+ $this->saved();
+
+ // reset values
+ $this->title = $this->description = '';
+ $this->isDisabled = $this->showOrder = $this->cost = $this->subscriptionLength = $this->isRecurring = 0;
+ $this->currency = 'EUR';
+ $this->groupIDs = array();
+ I18nHandler::getInstance()->reset();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * Saves i18n values.
+ *
+ * @param \wcf\data\paid\subscription\PaidSubscription $subscription
+ * @param string $columnName
+ */
+ public function saveI18nValue(PaidSubscription $subscription, $columnName) {
+ if (!I18nHandler::getInstance()->isPlainValue($columnName)) {
+ I18nHandler::getInstance()->save($columnName, 'wcf.paidSubscription.subscription'.$subscription->subscriptionID.($columnName == 'description' ? '.description' : ''), 'wcf.paidSubscription', 1);
+
+ // update database
+ $editor = new PaidSubscriptionEditor($subscription);
+ $editor->update(array(
+ $columnName => 'wcf.paidSubscription.subscription'.$subscription->subscriptionID.($columnName == 'description' ? '.description' : '')
+ ));
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ I18nHandler::getInstance()->assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'add',
+ 'isDisabled' => $this->isDisabled,
+ 'showOrder' => $this->showOrder,
+ 'cost' => $this->cost,
+ 'currency' => $this->currency,
+ 'subscriptionLength' => $this->subscriptionLength,
+ 'subscriptionLengthUnit' => $this->subscriptionLengthUnit,
+ 'isRecurring' => $this->isRecurring,
+ 'groupIDs' => $this->groupIDs,
+ 'excludedSubscriptionIDs' => $this->excludedSubscriptionIDs,
+ 'availableCurrencies' => $this->availableCurrencies,
+ 'availableUserGroups' => $this->availableUserGroups,
+ 'availableSubscriptions' => $this->availableSubscriptions
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\form\AbstractForm;
+use wcf\data\paid\subscription\PaidSubscriptionAction;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\system\language\I18nHandler;
+use wcf\system\WCF;
+use wcf\util\ArrayUtil;
+use wcf\data\paid\subscription\PaidSubscriptionList;
+
+/**
+ * Shows the paid subscription edit form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class PaidSubscriptionEditForm extends PaidSubscriptionAddForm {
+ /**
+ * id of the edited subscription
+ * @var integer
+ */
+ public $subscriptionID = 0;
+
+ /**
+ * edited subscription object
+ * @var \wcf\data\paid\subscription\PaidSubscription
+ */
+ public $subscription = null;
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ if (isset($_REQUEST['id'])) $this->subscriptionID = intval($_REQUEST['id']);
+ $this->subscription = new PaidSubscription($this->subscriptionID);
+ if (!$this->subscription->subscriptionID) {
+ throw new PermissionDeniedException();
+ }
+
+ parent::readParameters();
+ }
+
+ protected function getAvailableSubscriptions() {
+ $subscriptionList = new PaidSubscriptionList();
+ $subscriptionList->getConditionBuilder()->add('subscriptionID <> ?', array($this->subscriptionID));
+ $subscriptionList->sqlOrderBy = 'title';
+ $subscriptionList->readObjects();
+ $this->availableSubscriptions = $subscriptionList->getObjects();
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ I18nHandler::getInstance()->setOptions('description', 1, $this->subscription->description, 'wcf.paidSubscription.subscription\d+.description');
+ I18nHandler::getInstance()->setOptions('title', 1, $this->subscription->title, 'wcf.paidSubscription.subscription\d+');
+
+ $this->isDisabled = $this->subscription->isDisabled;
+ $this->showOrder = $this->subscription->showOrder;
+ $this->cost = $this->subscription->cost;
+ $this->currency = $this->subscription->currency;
+ $this->subscriptionLength = $this->subscription->subscriptionLength;
+ $this->subscriptionLengthUnit = $this->subscription->subscriptionLengthUnit;
+ $this->isRecurring = $this->subscription->isRecurring;
+ $this->groupIDs = explode(',', $this->subscription->groupIDs);
+ $this->excludedSubscriptionIDs = explode(',', $this->subscription->excludedSubscriptionIDs);
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ // read i18n values
+ I18nHandler::getInstance()->readValues();
+
+ // handle i18n plain input
+ if (I18nHandler::getInstance()->isPlainValue('description')) $this->description = I18nHandler::getInstance()->getValue('description');
+ if (I18nHandler::getInstance()->isPlainValue('title')) $this->title = I18nHandler::getInstance()->getValue('title');
+
+ if (!empty($_POST['isDisabled'])) $this->isDisabled = 1;
+ if (isset($_POST['showOrder'])) $this->showOrder = intval($_POST['showOrder']);
+ if (isset($_POST['cost'])) $this->cost = floatval($_POST['cost']);
+ if (isset($_POST['currency'])) $this->currency = $_POST['currency'];
+ if (!empty($_POST['subscriptionLengthPermanent'])) $this->subscriptionLengthPermanent = 1;
+ if (isset($_POST['subscriptionLength'])) $this->subscriptionLength = intval($_POST['subscriptionLength']);
+ if (isset($_POST['subscriptionLengthUnit'])) $this->subscriptionLengthUnit = $_POST['subscriptionLengthUnit'];
+ if (!empty($_POST['isRecurring'])) $this->isRecurring = 1;
+ if (isset($_POST['groupIDs']) && is_array($_POST['groupIDs'])) $this->groupIDs = ArrayUtil::toIntegerArray($_POST['groupIDs']);
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ AbstractForm::save();
+
+ // update description
+ $this->description = 'wcf.paidSubscription.subscription'.$this->subscription->subscriptionID.'.description';
+ if (I18nHandler::getInstance()->isPlainValue('description')) {
+ I18nHandler::getInstance()->remove($this->description);
+ $this->description = I18nHandler::getInstance()->getValue('description');
+ }
+ else {
+ I18nHandler::getInstance()->save('description', $this->description, 'wcf.paidSubscription', 1);
+ }
+
+ // update title
+ $this->title = 'wcf.paidSubscription.subscription'.$this->subscription->subscriptionID;
+ if (I18nHandler::getInstance()->isPlainValue('title')) {
+ I18nHandler::getInstance()->remove($this->title);
+ $this->title = I18nHandler::getInstance()->getValue('title');
+ }
+ else {
+ I18nHandler::getInstance()->save('title', $this->title, 'wcf.paidSubscription', 1);
+ }
+
+ // save subscription
+ $this->objectAction = new PaidSubscriptionAction(array($this->subscription), 'update', array('data' => array_merge($this->additionalFields, array(
+ 'title' => $this->title,
+ 'description' => $this->description,
+ 'isDisabled' => $this->isDisabled,
+ 'showOrder' => $this->showOrder,
+ 'cost' => $this->cost,
+ 'currency' => $this->currency,
+ 'subscriptionLength' => $this->subscriptionLength,
+ 'subscriptionLengthUnit' => $this->subscriptionLengthUnit,
+ 'isRecurring' => $this->isRecurring,
+ 'groupIDs' => implode(',', $this->groupIDs),
+ 'excludedSubscriptionIDs' => implode(',', $this->excludedSubscriptionIDs)
+ ))));
+ $this->objectAction->executeAction();
+ $this->saved();
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ $useRequestData = (empty($_POST)) ? false : true;
+ I18nHandler::getInstance()->assignVariables($useRequestData);
+
+ WCF::getTPL()->assign(array(
+ 'action' => 'edit',
+ 'subscriptionID' => $this->subscriptionID,
+ 'subscription' => $this->subscription
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\form;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\form\AbstractForm;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\system\exception\PermissionDeniedException;
+use wcf\util\StringUtil;
+use wcf\data\user\User;
+use wcf\data\paid\subscription\user\PaidSubscriptionUser;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserAction;
+use wcf\util\DateUtil;
+
+/**
+ * Shows the user subscription add form.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.form
+ * @category Community Framework
+ */
+class PaidSubscriptionUserAddForm extends AbstractForm {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.paidSubscription';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * subscription id
+ * @var integer
+ */
+ public $subscriptionID = 0;
+
+ /**
+ * subscription object
+ * @var \wcf\data\paid\subscription\PaidSubscription
+ */
+ public $subscription = null;
+
+ /**
+ * username
+ * @var string
+ */
+ public $username = '';
+
+ /**
+ * user object
+ * @var \wcf\data\user\User
+ */
+ public $user = null;
+
+ /**
+ * subscription end date
+ * @var string
+ */
+ public $endDate = '';
+
+ /**
+ * subscription end date
+ * @var \DateTime
+ */
+ public $endDateTime = null;
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->subscriptionID = intval($_REQUEST['id']);
+ $this->subscription = new PaidSubscription($this->subscriptionID);
+ if (!$this->subscription->subscriptionID) {
+ throw new PermissionDeniedException();
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::readFormParameters()
+ */
+ public function readFormParameters() {
+ parent::readFormParameters();
+
+ if (isset($_POST['username'])) $this->username = StringUtil::trim($_POST['username']);
+ if (isset($_POST['endDate'])) $this->endDate = $_POST['endDate'];
+ }
+
+ /**
+ * @see \wcf\form\IForm::validate()
+ */
+ public function validate() {
+ parent::validate();
+
+ if (empty($this->username)) {
+ throw new UserInputException('username');
+ }
+ $this->user = User::getUserByUsername($this->username);
+ if (!$this->user->userID) {
+ throw new UserInputException('username', 'notFound');
+ }
+
+ if ($this->subscription->subscrtionLength) {
+ $this->endDateTime = \DateTime::createFromFormat('Y-m-d', $this->endDate, new \DateTimeZone('UTC'));
+ if ($this->endDateTime === false || $this->endDateTime->getTimestamp() < TIME_NOW) {
+ throw new UserInputException('endDate');
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\form\IForm::save()
+ */
+ public function save() {
+ parent::save();
+
+ $userSubscription = PaidSubscriptionUser::getSubscriptionUser($this->subscriptionID, $this->user->userID);
+ $data = array();
+ if ($this->subscription->subscrtionLength) {
+ $data['endDate'] = $this->endDateTime->getTimestamp();
+ }
+ if ($userSubscription === null) {
+ // create new subscription
+ $action = new PaidSubscriptionUserAction(array(), 'create', array(
+ 'user' => $this->user,
+ 'subscription' => $this->subscription,
+ 'data' => $data
+ ));
+ $returnValues = $action->executeAction();
+ $userSubscription = $returnValues['returnValues'];
+ }
+ else {
+ // extend existing subscription
+ $action = new PaidSubscriptionUserAction(array($userSubscription), 'extend', array('data' => $data));
+ $action->executeAction();
+ }
+ $this->saved();
+
+ // reset values
+ $this->username = $this->endDate = '';
+
+ // show success
+ WCF::getTPL()->assign(array(
+ 'success' => true
+ ));
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ if (empty($_POST)) {
+ if ($this->subscription->subscriptionLength) {
+ $d = DateUtil::getDateTimeByTimestamp(TIME_NOW);
+ $d->add($this->subscription->getDateInterval());
+ $this->endDate = $d->format('Y-m-d');
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'subscriptionID' => $this->subscriptionID,
+ 'subscription' => $this->subscription,
+ 'username' => $this->username,
+ 'endDate' => $this->endDate
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+
+/**
+ * Shows the list of paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class PaidSubscriptionListPage extends SortablePage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.paidSubscription.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'showOrder';
+
+ /**
+ * @see \wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('subscriptionID', 'title', 'showOrder', 'cost', 'subscriptionLength');
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\paid\subscription\PaidSubscriptionList';
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+use wcf\util\StringUtil;
+use wcf\system\WCF;
+use wcf\system\cache\builder\PaidSubscriptionCacheBuilder;
+
+/**
+ * Shows the list of paid subscription transactions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class PaidSubscriptionTransactionLogListPage extends SortablePage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.paidSubscription.transactionLog.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'logTime';
+
+ /**
+ * @see \wcf\page\SortablePage::$defaultSortOrder
+ */
+ public $defaultSortOrder = 'DESC';
+
+ /**
+ * @see \wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('logID', 'subscriptionUserID', 'userID', 'subscriptionID', 'paymentMethodObjectTypeID', 'logTime', 'transactionID', 'logMessage');
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\paid\subscription\transaction\log\PaidSubscriptionTransactionLogList';
+
+ /**
+ * transaction id
+ * @var string
+ */
+ public $transactionID = '';
+
+ /**
+ * username
+ * @var string
+ */
+ public $username = '';
+
+ /**
+ * subscription id
+ * @var integer
+ */
+ public $subscriptionID = 0;
+
+ /**
+ * @see \wcf\page\AbstractPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['transactionID'])) $this->transactionID = StringUtil::trim($_REQUEST['transactionID']);
+ if (isset($_REQUEST['username'])) $this->username = StringUtil::trim($_REQUEST['username']);
+ if (isset($_REQUEST['subscriptionID'])) $this->subscriptionID = intval($_REQUEST['subscriptionID']);
+ }
+
+ /**
+ * Initializes DatabaseObjectList instance.
+ */
+ protected function initObjectList() {
+ parent::initObjectList();
+
+ if ($this->transactionID) {
+ $this->objectList->getConditionBuilder()->add('paid_subscription_transaction_log.transactionID LIKE ?', array('%' . $this->transactionID . '%'));
+ }
+ if ($this->username) {
+ $this->objectList->getConditionBuilder()->add('paid_subscription_transaction_log.userID IN (SELECT userID FROM wcf'.WCF_N.'_user WHERE username LIKE ?)', array('%' . $this->username . '%'));
+ }
+ if ($this->subscriptionID) {
+ $this->objectList->getConditionBuilder()->add('paid_subscription_transaction_log.subscriptionID = ?', array($this->subscriptionID));
+ }
+
+ $this->objectList->sqlSelects = 'user_table.username, paid_subscription.title';
+ $this->objectList->sqlJoins = "LEFT JOIN wcf".WCF_N."_user user_table ON (user_table.userID = paid_subscription_transaction_log.userID)";
+ $this->objectList->sqlJoins .= " LEFT JOIN wcf".WCF_N."_paid_subscription paid_subscription ON (paid_subscription.subscriptionID = paid_subscription_transaction_log.subscriptionID)";
+ }
+
+ /**
+ * @see \wcf\page\AbstractPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'transactionID' => $this->transactionID,
+ 'username' => $this->username,
+ 'subscriptionID' => $this->subscriptionID,
+ 'availableSubscriptions' => PaidSubscriptionCacheBuilder::getInstance()->getData()
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\data\paid\subscription\transaction\log\PaidSubscriptionTransactionLog;
+use wcf\page\AbstractPage;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+
+/**
+ * Shows transaction details.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class PaidSubscriptionTransactionLogPage extends AbstractPage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.paidSubscription';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ if (isset($_REQUEST['id'])) $this->logID = intval($_REQUEST['id']);
+ $this->log = new PaidSubscriptionTransactionLog($this->logID);
+ if (!$this->log->logID) {
+ throw new IllegalLinkException();
+ }
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'logID' => $this->logID,
+ 'log' => $this->log
+ ));
+ }
+}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\SortablePage;
+
+/**
+ * Shows the list of paid subscription users.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage acp.page
+ * @category Community Framework
+ */
+class PaidSubscriptionUserListPage extends SortablePage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.paidSubscription.user.list';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\page\SortablePage::$defaultSortField
+ */
+ public $defaultSortField = 'userID';
+
+ /**
+ * @see \wcf\page\SortablePage::$validSortFields
+ */
+ public $validSortFields = array('subscriptionUserID', 'userID', 'subscriptionID', 'startDate', 'endDate');
+
+ /**
+ * @see \wcf\page\MultipleLinkPage::$objectListClassName
+ */
+ public $objectListClassName = 'wcf\data\paid\subscription\user\PaidSubscriptionUserList';
+
+ /**
+ * Initializes DatabaseObjectList instance.
+ */
+ protected function initObjectList() {
+ parent::initObjectList();
+
+ $this->objectList->getConditionBuilder()->add('isActive = ?', array(1));
+ $this->objectList->sqlSelects = 'user_table.username, paid_subscription.title';
+ $this->objectList->sqlJoins = "LEFT JOIN wcf".WCF_N."_user user_table ON (user_table.userID = paid_subscription_user.userID)";
+ $this->objectList->sqlJoins .= " LEFT JOIN wcf".WCF_N."_paid_subscription paid_subscription ON (paid_subscription.subscriptionID = paid_subscription_user.subscriptionID)";
+ }
+}
--- /dev/null
+<?php
+namespace wcf\action;
+use wcf\action\AbstractAction;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\exception\SystemException;
+use wcf\system\payment\type\IPaymentType;
+use wcf\util\HTTPRequest;
+
+/**
+ * Handles Paypal callbacks.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage action
+ * @category Community Framework
+ */
+class PaypalCallbackAction extends AbstractAction {
+ /**
+ * @see \wcf\action\IAction::execute()
+ */
+ public function execute() {
+ parent::execute();
+
+ // check response
+ $processor = null;
+ try {
+ // post back to paypal to validate
+ $content = '';
+ try {
+ $request = new HTTPRequest('http://www.paypal.com', array(), array_merge(array('cmd' => '_notify-validate'), $_POST));
+ $request->execute();
+ $reply = $request->getReply();
+ $content = $reply['body'];
+ }
+ catch (SystemException $e) {
+ throw new SystemException('connection to paypal.com failed');
+ }
+
+ if (strstr($content, "VERIFIED") === false) {
+ throw new SystemException('request not validated');
+ }
+
+ // Check that receiver_email is your Primary PayPal email
+ if (!isset($_POST['receiver_email']) || (strtolower($_POST['receiver_email']) != strtolower(PAYPAL_EMAIL_ADDRESS))) {
+ throw new SystemException('invalid receiver_email');
+ }
+
+ // get token
+ if (!isset($_POST['custom'])) {
+ throw new SystemException('invalid custom item');
+ }
+ $tokenParts = explode(':', $_POST['custom'], 2);
+ if (count($tokenParts) != 2) {
+ throw new SystemException('invalid custom item');
+ }
+ // get payment type object type
+ $objectType = ObjectTypeCache::getInstance()->getObjectType(intval($tokenParts[0]));
+ if ($objectType === null || !($objectType->getProcessor() instanceof IPaymentType)) {
+ throw new SystemException('invalid payment type id');
+ }
+ $processor = $objectType->getProcessor();
+
+ // get status
+ $status = '';
+ if ($_POST['txn_type'] == 'web_accept' || $_POST['txn_type'] == 'subscr_payment') {
+ if ($_POST['payment_status'] == 'Completed') {
+ $status = 'completed';
+ }
+ }
+ if ($_POST['payment_status'] == 'Refunded' || $_POST['payment_status'] == 'Reversed') {
+ $status = 'reversed';
+ }
+ if ($_POST['payment_status'] == 'Canceled_Reversal') {
+ $status = 'canceled_reversal';
+ }
+
+ if ($status) {
+ $processor->processTransaction(ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.payment.method', 'com.woltlab.wcf.payment.method.paypal'), $tokenParts[1], $_POST['mc_gross'], $_POST['mc_currency'], $_POST['txn_id'], $status, $_POST);
+ }
+ }
+ catch (SystemException $e) {
+ @header('HTTP/1.1 500 Internal Server Error');
+ echo $e->getMessage();
+ exit;
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\DatabaseObject;
+use wcf\system\payment\method\PaymentMethodHandler;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+
+/**
+ * Represents a paid subscription.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription
+ * @category Community Framework
+ */
+class PaidSubscription extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'paid_subscription';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseIndexName
+ */
+ protected static $databaseTableIndexName = 'subscriptionID';
+
+ /**
+ * Returns list of purchase buttons.
+ *
+ * @return array<string>
+ */
+ public function getPurchaseButtons() {
+ $objectTypeID = ObjectTypeCache::getInstance()->getObjectTypeIDByName('com.woltlab.wcf.payment.type', 'com.woltlab.wcf.payment.type.paidSubscription');
+ $buttons = array();
+ foreach (PaymentMethodHandler::getInstance()->getPaymentMethods() as $paymentMethod) {
+ // check if payment method supports recurring payments
+ if ($this->isRecurring && !$paymentMethod->supportsRecurringPayments()) continue;
+
+ // check supported currencies
+ if (!in_array($this->currency, $paymentMethod->getSupportedCurrencies())) continue;
+
+ $buttons[] = $paymentMethod->getPurchaseButton($this->cost, $this->currency, WCF::getLanguage()->get($this->title), $objectTypeID . ':' . WCF::getUser()->userID . ':' . $this->subscriptionID , LinkHandler::getInstance()->getLink('PaidSubscriptionReturn'), LinkHandler::getInstance()->getLink(), $this->isRecurring, $this->subscriptionLength, $this->subscriptionLengthUnit);
+ }
+
+ return $buttons;
+ }
+
+ /**
+ * Returns a DateInterval object based on subscription length.
+ *
+ * @return \DateInterval
+ */
+ public function getDateInterval() {
+ return new \DateInterval('P' . $this->subscriptionLength . $this->subscriptionLengthUnit);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\data\IToggleAction;
+
+/**
+ * Executes paid subscription-related actions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription
+ * @category Community Framework
+ */
+class PaidSubscriptionAction extends AbstractDatabaseObjectAction implements IToggleAction {
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
+ */
+ protected $requireACP = array('create', 'delete', 'toggle', 'update');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::create()
+ */
+ public function create() {
+ $showOrder = 0;
+ if (isset($this->parameters['data']['showOrder'])) {
+ $showOrder = $this->parameters['data']['showOrder'];
+ unset($this->parameters['data']['showOrder']);
+ }
+
+ $subscription = parent::create();
+ $editor = new PaidSubscriptionEditor($subscription);
+ $editor->setShowOrder($showOrder);
+
+ return new PaidSubscription($subscription->subscriptionID);
+ }
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::update()
+ */
+ public function update() {
+ parent::update();
+
+ if (count($this->objects) == 1 && isset($this->parameters['data']['showOrder']) && $this->parameters['data']['showOrder'] != reset($this->objects)->showOrder) {
+ reset($this->objects)->setShowOrder($this->parameters['data']['showOrder']);
+ }
+ }
+
+ /**
+ * @see \wcf\data\IToggleAction::toggle()
+ */
+ public function toggle() {
+ foreach ($this->objects as $object) {
+ $object->update(array(
+ 'isDisabled' => $object->isDisabled ? 0 : 1
+ ));
+ }
+ }
+
+ /**
+ * @see \wcf\data\IToggleAction::validateToggle()
+ */
+ public function validateToggle() {
+ parent::validateUpdate();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription;
+use wcf\data\DatabaseObjectEditor;
+use wcf\data\IEditableCachedObject;
+use wcf\system\cache\builder\PaidSubscriptionCacheBuilder;
+use wcf\system\WCF;
+
+/**
+ * Provides functions to edit paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription
+ * @category Community Framework
+ */
+class PaidSubscriptionEditor extends DatabaseObjectEditor implements IEditableCachedObject {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\paid\subscription\PaidSubscription';
+
+ /**
+ * Sets the show order of the subscription.
+ *
+ * @param integer $showOrder
+ */
+ public function setShowOrder($showOrder = 0) {
+ $newShowOrder = 1;
+
+ $sql = "SELECT MAX(showOrder)
+ FROM wcf".WCF_N."_paid_subscription";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute();
+ $maxShowOrder = $statement->fetchColumn();
+ if (!$maxShowOrder) $maxShowOrder = 0;
+
+ if (!$showOrder || $showOrder > $maxShowOrder) {
+ $newShowOrder = $maxShowOrder + 1;
+ }
+ else {
+ // shift other subscriptions
+ $sql = "UPDATE wcf".WCF_N."_paid_subscription
+ SET showOrder = showOrder + 1
+ WHERE showOrder >= ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array(
+ $showOrder
+ ));
+
+ $newShowOrder = $showOrder;
+ }
+
+ $this->update(array(
+ 'showOrder' => $newShowOrder
+ ));
+ }
+
+ /**
+ * @see \wcf\data\IEditableCachedObject::resetCache()
+ */
+ public static function resetCache() {
+ PaidSubscriptionCacheBuilder::getInstance()->reset();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription
+ * @category Community Framework
+ */
+class PaidSubscriptionList extends DatabaseObjectList { }
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\transaction\log;
+use wcf\data\DatabaseObject;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\data\user\User;
+use wcf\system\WCF;
+
+/**
+ * Represents a paid subscription transaction log entry.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.transaction.log
+ * @category Community Framework
+ */
+class PaidSubscriptionTransactionLog extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'paid_subscription_transaction_log';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseIndexName
+ */
+ protected static $databaseTableIndexName = 'logID';
+
+ /**
+ * user object
+ * @var \wcf\data\user\User
+ */
+ protected $user = null;
+
+ /**
+ * paid subscription object
+ * @var \wcf\data\paid\subscription\PaidSubscription
+ */
+ protected $subscription = null;
+
+ /**
+ * Returns the payment method of this transaction.
+ *
+ * @return string
+ */
+ public function getPaymentMethodName() {
+ $objectType = ObjectTypeCache::getInstance()->getObjectType($this->paymentMethodObjectTypeID);
+ return $objectType->objectType;
+ }
+
+ /**
+ * Returns transaction details.
+ *
+ * @return array
+ */
+ public function getTransactionDetails() {
+ return unserialize($this->transactionDetails);
+ }
+
+ /**
+ * Returns the user of this transaction.
+ *
+ * @return \wcf\data\user\User
+ */
+ public function getUser() {
+ if ($this->user === null) {
+ $this->user = new User($this->userID);
+ }
+
+ return $this->user;
+ }
+
+ /**
+ * Returns the paid subscription of this transaction.
+ *
+ * @return \wcf\data\paid\subscription\PaidSubscription
+ */
+ public function getSubscription() {
+ if ($this->subscription === null) {
+ $this->subscription = new PaidSubscription($this->subscriptionID);
+ }
+
+ return $this->subscription;
+ }
+
+ /**
+ * Gets a transaction log entry by transaction id.
+ *
+ * @param integer $paymentMethodObjectTypeID
+ * @param string $transactionID
+ * @return \wcf\data\paid\subscription\transaction\log\PaidSubscriptionTransactionLog
+ */
+ public static function getLogByTransactionID($paymentMethodObjectTypeID, $transactionID) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_paid_subscription_transaction_log
+ WHERE paymentMethodObjectTypeID = ?
+ AND transactionID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($paymentMethodObjectTypeID, $transactionID));
+ $row = $statement->fetchArray();
+ if ($row !== false) {
+ return new PaidSubscriptionTransactionLog(null, $row);
+ }
+
+ return null;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\transaction\log;
+use wcf\data\AbstractDatabaseObjectAction;
+
+/**
+ * Executes paid subscription transaction log-related actions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.transaction.log
+ * @category Community Framework
+ */
+class PaidSubscriptionTransactionLogAction extends AbstractDatabaseObjectAction {}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\transaction\log;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit paid subscription transaction log entries.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.transaction.log
+ * @category Community Framework
+ */
+class PaidSubscriptionTransactionLogEditor extends DatabaseObjectEditor {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\paid\subscription\transaction\log\PaidSubscriptionTransactionLog';
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\transaction\log;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of paid subscription transaction log entries.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.transaction.log
+ * @category Community Framework
+ */
+class PaidSubscriptionTransactionLogList extends DatabaseObjectList { }
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\user;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\data\DatabaseObject;
+use wcf\system\WCF;
+
+/**
+ * Represents a paid subscription user.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.user
+ * @category Community Framework
+ */
+class PaidSubscriptionUser extends DatabaseObject {
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseTableName
+ */
+ protected static $databaseTableName = 'paid_subscription_user';
+
+ /**
+ * @see \wcf\data\DatabaseObject::$databaseIndexName
+ */
+ protected static $databaseTableIndexName = 'subscriptionUserID';
+
+ /**
+ * paid subscription object
+ * @var \wcf\data\paid\subscription\PaidSubscription
+ */
+ protected $subscription = null;
+
+ /**
+ * Gets the paid subscription object.
+ *
+ * @return \wcf\data\paid\subscription\PaidSubscription
+ */
+ public function getSubscription() {
+ if ($this->subscription === null) {
+ $this->subscription = new PaidSubscription($this->subscriptionID);
+ }
+
+ return $this->subscription;
+ }
+
+ /**
+ * Sets the paid subscription object.
+ *
+ * @param \wcf\data\paid\subscription\PaidSubscription $subscription
+ */
+ public function setSubscription(PaidSubscription $subscription) {
+ $this->subscription = $subscription;
+ }
+
+ /**
+ * Gets a specific subscription user.
+ *
+ * @param integer $subscriptionID
+ * @param integer $userID
+ * @return \wcf\data\paid\subscription\user\PaidSubscriptionUser
+ */
+ public static function getSubscriptionUser($subscriptionID, $userID) {
+ $sql = "SELECT *
+ FROM wcf".WCF_N."_paid_subscription_user
+ WHERE subscriptionID = ?
+ AND userID = ?";
+ $statement = WCF::getDB()->prepareStatement($sql);
+ $statement->execute(array($subscriptionID, $userID));
+ $row = $statement->fetchArray();
+ if ($row !== false) {
+ return new PaidSubscriptionUser(null, $row);
+ }
+
+ return null;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\user;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\data\user\group\UserGroup;
+use wcf\data\user\UserAction;
+use wcf\data\user\User;
+use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\UserInputException;
+use wcf\util\DateUtil;
+
+/**
+ * Executes paid subscription user-related actions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.user
+ * @category Community Framework
+ */
+class PaidSubscriptionUserAction extends AbstractDatabaseObjectAction {
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
+ */
+ protected $permissionsDelete = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
+ */
+ protected $permissionsUpdate = array('admin.paidSubscription.canManageSubscription');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::$requireACP
+ */
+ protected $requireACP = array('create', 'delete', 'update');
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::create()
+ */
+ public function create() {
+ $this->parameters['data']['subscriptionID'] = $this->parameters['subscription']->subscriptionID;
+ $this->parameters['data']['userID'] = $this->parameters['user']->userID;
+ if (!isset($this->parameters['data']['startDate'])) $this->parameters['data']['startDate'] = TIME_NOW;
+ if (!isset($this->parameters['data']['endDate'])) {
+ if (!$this->parameters['subscription']->subscriptionLength) {
+ $this->parameters['data']['endDate'] = 0;
+ }
+ else {
+ $d = DateUtil::getDateTimeByTimestamp($this->parameters['data']['startDate']);
+ $d->add($this->parameters['subscription']->getDateInterval());
+ $this->parameters['data']['endDate'] = $d->getTimestamp();
+ }
+ }
+ if (!isset($this->parameters['data']['isActive'])) $this->parameters['data']['isActive'] = 1;
+
+ $subscriptionUser = parent::create();
+
+ // update group memberships
+ $action = new PaidSubscriptionUserAction(array($subscriptionUser), 'addGroupMemberships');
+ $action->executeAction();
+
+ return $subscriptionUser;
+ }
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::validateCreate()
+ */
+ public function validateCreate() {
+ parent::validateCreate();
+
+ if (!isset($this->parameters['subscription']) || !($this->parameters['subscription'] instanceof PaidSubscription)) {
+ throw new UserInputException('subscription');
+ }
+ if (!isset($this->parameters['user']) || !($this->parameters['user'] instanceof User)) {
+ throw new UserInputException('user');
+ }
+ }
+
+ /**
+ * Extends an existing subscription.
+ */
+ public function extend() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ $endDate = 0;
+ if (!isset($this->parameters['data']['endDate'])) {
+ $subscription = $subscriptionUser->getSubscription();
+ if ($subscription->subscriptionLength) {
+ $d = DateUtil::getDateTimeByTimestamp(TIME_NOW);
+ $d->add($subscription->getDateInterval());
+ $endDate = $d->getTimestamp();
+ }
+ }
+ else {
+ $endDate = $this->parameters['data']['endDate'];
+ }
+
+ $subscriptionUser->update(array(
+ 'endDate' => $endDate,
+ 'isActive' => 1
+ ));
+
+ if (!$subscriptionUser->isActive) {
+ // update group memberships
+ $action = new PaidSubscriptionUserAction(array($subscriptionUser), 'addGroupMemberships');
+ $action->executeAction();
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\data\AbstractDatabaseObjectAction::delete()
+ */
+ public function delete() {
+ $this->revoke();
+
+ parent::delete();
+ }
+
+ /**
+ * Revokes an existing subscription.
+ */
+ public function revoke() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ $subscriptionUser->update(array('isActive' => 0));
+
+ // update group memberships
+ $action = new PaidSubscriptionUserAction(array($subscriptionUser), 'removeGroupMemberships');
+ $action->executeAction();
+ }
+ }
+
+ /**
+ * Validates the revoke action.
+ */
+ public function validateRevoke() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ if (!$subscriptionUser->isActive) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+ }
+
+ /**
+ * Restores an existing subscription.
+ */
+ public function restore() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ $subscriptionUser->update(array('isActive' => 1));
+
+ // update group memberships
+ $action = new PaidSubscriptionUserAction(array($subscriptionUser), 'addGroupMemberships');
+ $action->executeAction();
+ }
+ }
+
+ /**
+ * Validates the restore action.
+ */
+ public function validateRestore() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ if ($subscriptionUser->isActive) {
+ throw new UserInputException('objectIDs');
+ }
+ }
+ }
+
+ /**
+ * Applies group memberships.
+ */
+ public function addGroupMemberships() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ $groupIDs = array();
+ foreach (explode(',', $subscriptionUser->getSubscription()->groupIDs) as $groupID) {
+ if (UserGroup::getGroupByID($groupID) !== null) {
+ $groupIDs[] = $groupID;
+ }
+ }
+ if (!empty($groupIDs)) {
+ $action = new UserAction(array($subscriptionUser->userID), 'addToGroups', array(
+ 'groups' => $groupIDs,
+ 'deleteOldGroups' => false,
+ 'addDefaultGroups' => false
+ ));
+ $action->executeAction();
+ }
+ }
+ }
+
+ /**
+ * Removes group memberships.
+ */
+ public function removeGroupMemberships() {
+ if (empty($this->objects)) {
+ $this->readObjects();
+ }
+
+ foreach ($this->objects as $subscriptionUser) {
+ $groupIDs = array();
+ foreach (explode(',', $subscriptionUser->getSubscription()->groupIDs) as $groupID) {
+ if (UserGroup::getGroupByID($groupID) !== null) {
+ $groupIDs[] = $groupID;
+ }
+ }
+ if (!empty($groupIDs)) {
+ $action = new UserAction(array($subscriptionUser->userID), 'removeFromGroups', array(
+ 'groups' => $groupIDs,
+ ));
+ $action->executeAction();
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\user;
+use wcf\data\DatabaseObjectEditor;
+
+/**
+ * Provides functions to edit paid subscription users.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.user
+ * @category Community Framework
+ */
+class PaidSubscriptionUserEditor extends DatabaseObjectEditor {
+ /**
+ * @see \wcf\data\DatabaseObjectDecorator::$baseClass
+ */
+ protected static $baseClass = 'wcf\data\paid\subscription\user\PaidSubscriptionUser';
+}
--- /dev/null
+<?php
+namespace wcf\data\paid\subscription\user;
+use wcf\data\DatabaseObjectList;
+
+/**
+ * Represents a list of paid subscription users.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage data.paid.subscription.user
+ * @category Community Framework
+ */
+class PaidSubscriptionUserList extends DatabaseObjectList { }
--- /dev/null
+<?php
+namespace wcf\page;
+use wcf\system\cache\builder\PaidSubscriptionCacheBuilder;
+use wcf\system\menu\user\UserMenu;
+use wcf\system\WCF;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+
+/**
+ * Shows a list of the available paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage page
+ * @category Community Framework
+ */
+class PaidSubscriptionListPage extends AbstractPage {
+ /**
+ * @see \wcf\page\AbstractPage::$loginRequired
+ */
+ public $loginRequired = true;
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededModules
+ */
+ public $neededModules = array('MODULE_PAID_SUBSCRIPTION');
+
+ /**
+ * list of available paid subscriptions
+ * @var array
+ */
+ public $subscriptions = array();
+
+ /**
+ * list of user subscriptions
+ * @var \wcf\data\paid\subscription\user\PaidSubscriptionUserList
+ */
+ public $userSubscriptionList = array();
+
+ /**
+ * @see \wcf\page\AbstractPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ // get available subscriptions
+ $this->subscriptions = PaidSubscriptionCacheBuilder::getInstance()->getData();
+
+ // get user subscriptions
+ $this->userSubscriptionList = new PaidSubscriptionUserList();
+ $this->userSubscriptionList->getConditionBuilder()->add('userID = ?', array(WCF::getUser()->userID));
+ $this->userSubscriptionList->getConditionBuilder()->add('isActive = ?', array(1));
+ $this->userSubscriptionList->readObjects();
+
+ foreach ($this->userSubscriptionList as $userSubscription) {
+ if (isset($this->subscriptions[$userSubscription->subscriptionID])) {
+ $userSubscription->setSubscription($this->subscriptions[$userSubscription->subscriptionID]);
+ unset($this->subscriptions[$userSubscription->subscriptionID]);
+ }
+ }
+ foreach ($this->userSubscriptionList as $userSubscription) {
+ if ($userSubscription->getSubscription()->excludedSubscriptionIDs) {
+ foreach (explode(',', $userSubscription->getSubscription()->excludedSubscriptionIDs) as $subscriptionID) {
+ if (isset($this->subscriptions[$subscriptionID])) unset($this->subscriptions[$subscriptionID]);
+ }
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\page\AbstractPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'subscriptions' => $this->subscriptions,
+ 'userSubscriptions' => $this->userSubscriptionList
+ ));
+ }
+
+ /**
+ * @see \wcf\page\Page::show()
+ */
+ public function show() {
+ // set active tab
+ UserMenu::getInstance()->setActiveMenuItem('wcf.user.menu.settings.paidSubscription');
+
+ parent::show();
+ }
+}
--- /dev/null
+<?php
+namespace wcf\page;
+use wcf\system\WCF;
+
+/**
+ * Shows the paid subscription return message.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage page
+ * @category Community Framework
+ */
+class PaidSubscriptionReturn extends AbstractPage {
+ /**
+ * @see \wcf\page\AbstractPage::$templateName
+ */
+ public $templateName = 'redirect';
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'message' => WCF::getLanguage()->get('wcf.paidSubscription.returnMessage')
+ ));
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace wcf\system\cache\builder;
+use wcf\data\paid\subscription\PaidSubscriptionList;
+
+/**
+ * Caches the paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.cache.builder
+ * @category Community Framework
+ */
+class PaidSubscriptionCacheBuilder extends AbstractCacheBuilder {
+ /**
+ * @see \wcf\system\cache\builder\AbstractCacheBuilder::rebuild()
+ */
+ protected function rebuild(array $parameters) {
+ $subscriptionList = new PaidSubscriptionList();
+ $subscriptionList->getConditionBuilder()->add('isDisabled = ?', array(0));
+ $subscriptionList->sqlOrderBy = 'showOrder';
+ $subscriptionList->readObjects();
+
+ return $subscriptionList->getObjects();
+ }
+}
<?php
namespace wcf\system\cronjob;
+use wcf\data\cronjob\Cronjob;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserAction;
/**
* Cronjob for a hourly system cleanup.
* @subpackage system.cronjob
* @category Community Framework
*/
-class HourlyCleanUpCronjob extends AbstractCronjob { }
+class HourlyCleanUpCronjob extends AbstractCronjob {
+ /**
+ * @see \wcf\system\cronjob\ICronjob::execute()
+ */
+ public function execute(Cronjob $cronjob) {
+ // disable expired paid subscriptions
+ if (MODULE_PAID_SUBSCRIPTION) {
+ $subscriptionUser = new PaidSubscriptionUserList();
+ $subscriptionUser->getConditionBuilder()->add('isActive = ?', array(1));
+ $subscriptionUser->getConditionBuilder()->add('endDate > 0 AND endDate < ?', array(TIME_NOW));
+ $subscriptionUser->readObjects();
+
+ if (count($subscriptionUser->getObjects())) {
+ $action = new PaidSubscriptionUserAction(array($subscriptionUser->getObjects()), 'revoke');
+ }
+ }
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\dashboard\box;
+use wcf\data\dashboard\box\DashboardBox;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+use wcf\page\IPage;
+use wcf\system\cache\builder\PaidSubscriptionCacheBuilder;
+use wcf\system\WCF;
+
+/**
+ * Dashboard box for paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.dashboard.box
+ * @category Community Framework
+ */
+class PaidSubscriptionsDashboardBox extends AbstractContentDashboardBox {
+ /**
+ * list of available paid subscriptions
+ * @var array
+ */
+ public $subscriptions = array();
+
+ /**
+ * @see \wcf\system\dashboard\box\IDashboardBox::init()
+ */
+ public function init(DashboardBox $box, IPage $page) {
+ parent::init($box, $page);
+
+ if (WCF::getUser()->userID && MODULE_PAID_SUBSCRIPTION) {
+ // get available subscriptions
+ $this->subscriptions = PaidSubscriptionCacheBuilder::getInstance()->getData();
+
+ // get purchased subscriptions
+ $userSubscriptionList = new PaidSubscriptionUserList();
+ $userSubscriptionList->getConditionBuilder()->add('userID = ?', array(WCF::getUser()->userID));
+ $userSubscriptionList->getConditionBuilder()->add('isActive = ?', array(1));
+ $userSubscriptionList->readObjects();
+
+ // remove purchased subscriptions
+ foreach ($userSubscriptionList as $userSubscription) {
+ if (isset($this->subscriptions[$userSubscription->subscriptionID])) {
+ $userSubscription->setSubscription($this->subscriptions[$userSubscription->subscriptionID]);
+ unset($this->subscriptions[$userSubscription->subscriptionID]);
+ }
+ }
+ // remove excluded subscriptions
+ foreach ($userSubscriptionList as $userSubscription) {
+ if ($userSubscription->getSubscription()->excludedSubscriptionIDs) {
+ foreach (explode(',', $userSubscription->getSubscription()->excludedSubscriptionIDs) as $subscriptionID) {
+ if (isset($this->subscriptions[$subscriptionID])) unset($this->subscriptions[$subscriptionID]);
+ }
+ }
+ }
+ }
+ $this->fetched();
+ }
+
+ /**
+ * @see \wcf\system\dashboard\box\AbstractContentDashboardBox::render()
+ */
+ protected function render() {
+ if (!empty($this->subscriptions)) {
+ WCF::getTPL()->assign(array(
+ 'subscriptions' => $this->subscriptions
+ ));
+
+ return WCF::getTPL()->fetch('dashboardBoxPaidSubscriptions');
+ }
+
+ return '';
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\dashboard\box;
+use wcf\data\dashboard\box\DashboardBox;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserList;
+use wcf\page\IPage;
+use wcf\system\cache\builder\PaidSubscriptionCacheBuilder;
+use wcf\system\WCF;
+
+/**
+ * Dashboard box for paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.dashboard.box
+ * @category Community Framework
+ */
+class PaidSubscriptionsSidebarDashboardBox extends AbstractSidebarDashboardBox {
+ /**
+ * list of available paid subscriptions
+ * @var array
+ */
+ public $subscriptions = array();
+
+ /**
+ * @see \wcf\system\dashboard\box\IDashboardBox::init()
+ */
+ public function init(DashboardBox $box, IPage $page) {
+ parent::init($box, $page);
+
+ if (WCF::getUser()->userID && MODULE_PAID_SUBSCRIPTION) {
+ // get available subscriptions
+ $this->subscriptions = PaidSubscriptionCacheBuilder::getInstance()->getData();
+
+ // get purchased subscriptions
+ $userSubscriptionList = new PaidSubscriptionUserList();
+ $userSubscriptionList->getConditionBuilder()->add('userID = ?', array(WCF::getUser()->userID));
+ $userSubscriptionList->getConditionBuilder()->add('isActive = ?', array(1));
+ $userSubscriptionList->readObjects();
+
+ // remove purchased subscriptions
+ foreach ($userSubscriptionList as $userSubscription) {
+ if (isset($this->subscriptions[$userSubscription->subscriptionID])) {
+ $userSubscription->setSubscription($this->subscriptions[$userSubscription->subscriptionID]);
+ unset($this->subscriptions[$userSubscription->subscriptionID]);
+ }
+ }
+ // remove excluded subscriptions
+ foreach ($userSubscriptionList as $userSubscription) {
+ if ($userSubscription->getSubscription()->excludedSubscriptionIDs) {
+ foreach (explode(',', $userSubscription->getSubscription()->excludedSubscriptionIDs) as $subscriptionID) {
+ if (isset($this->subscriptions[$subscriptionID])) unset($this->subscriptions[$subscriptionID]);
+ }
+ }
+ }
+ }
+ $this->fetched();
+ }
+
+ /**
+ * @see \wcf\system\dashboard\box\AbstractContentDashboardBox::render()
+ */
+ protected function render() {
+ if (!empty($this->subscriptions)) {
+ WCF::getTPL()->assign(array(
+ 'subscriptions' => $this->subscriptions
+ ));
+
+ return WCF::getTPL()->fetch('dashboardBoxPaidSubscriptionsSidebar');
+ }
+
+ return '';
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\option;
+use wcf\data\option\Option;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\system\payment\method\PaymentMethodHandler;
+
+/**
+ * Option type implementation for selecting payment methods.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.option
+ * @category Community Framework
+ */
+class PaymentMethodSelectOptionType extends AbstractOptionType {
+ /**
+ * @see \wcf\system\option\IOptionType::getFormElement()
+ */
+ public function getFormElement(Option $option, $value) {
+ $selectOptions = PaymentMethodHandler::getInstance()->getPaymentMethodSelection();
+
+ return WCF::getTPL()->fetch('paymentMethodSelectOptionType', 'wcf', array(
+ 'selectOptions' => $selectOptions,
+ 'option' => $option,
+ 'value' => explode(',', $value)
+ ));
+ }
+
+ /**
+ * @see \wcf\system\option\IOptionType::validate()
+ */
+ public function validate(Option $option, $newValue) {
+ if (!is_array($newValue)) {
+ $newValue = array();
+ }
+
+ $selectOptions = PaymentMethodHandler::getInstance()->getPaymentMethodSelection();
+ foreach ($newValue as $optionName) {
+ if (!isset($selectOptions[$optionName])) {
+ throw new UserInputException($option->optionName, 'validationFailed');
+ }
+ }
+ }
+
+ /**
+ * @see \wcf\system\option\IOptionType::getData()
+ */
+ public function getData(Option $option, $newValue) {
+ if (!is_array($newValue)) return '';
+ return implode(',', $newValue);
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\payment\method;
+
+/**
+ * Abstract implementation of a payment method.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.method
+ * @category Community Framework
+ */
+abstract class AbstractPaymentMethod implements IPaymentMethod {
+ /**
+ * @see \wcf\system\payment\method\IPaymentMethod::supportsRecurringPayments()
+ */
+ public function supportsRecurringPayments() {
+ return false;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\payment\method;
+
+/**
+ * Default interface for payment methods.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.method
+ * @category Community Framework
+ */
+interface IPaymentMethod {
+ /**
+ * Returns true, if this payment method supports recurring payments.
+ *
+ * @return boolean
+ */
+ public function supportsRecurringPayments();
+
+ /**
+ * Returns a list of supported currencies.
+ *
+ * @return array<string>
+ */
+ public function getSupportedCurrencies();
+
+ /**
+ * Returns the HTML code of the purchase button.
+ *
+ * @param float $cost
+ * @param string $currency ISO 4217 code
+ * @param string $name product/item name
+ * @param string $token custom token
+ * @param string $returnURL
+ * @param string $cancelReturnURL
+ * @param boolean $isRecurring
+ * @param integer $subscriptionLength
+ * @param string $subscriptionLengthUnit
+ *
+ * @return string
+ */
+ public function getPurchaseButton($cost, $currency, $name, $token, $returnURL, $cancelReturnURL, $isRecurring = false, $subscriptionLength = 0, $subscriptionLengthUnit = '');
+}
--- /dev/null
+<?php
+namespace wcf\system\payment\method;
+use wcf\data\object\type\ObjectTypeCache;
+use wcf\system\SingletonFactory;
+use wcf\system\WCF;
+
+/**
+ * Handles enabled/available payment methods.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.method
+ * @category Community Framework
+ */
+class PaymentMethodHandler extends SingletonFactory {
+ /**
+ * payment methods
+ * @var array
+ */
+ protected $paymentMethods = array();
+
+ /**
+ * payment method object types
+ * @var array
+ */
+ protected $objectTypes = array();
+
+ /**
+ * @see \wcf\system\SingletonFactory::init()
+ */
+ protected function init() {
+ $availablePaymentMethods = explode(',', AVAILABLE_PAYMENT_METHODS);
+ $this->objectTypes = ObjectTypeCache::getInstance()->getObjectTypes('com.woltlab.wcf.payment.method');
+ foreach ($this->objectTypes as $objectType) {
+ if (in_array($objectType->objectType, $availablePaymentMethods)) {
+ $this->paymentMethods[] = $objectType->getProcessor();
+ }
+ }
+ }
+
+ /**
+ * Returns the available payment methods.
+ *
+ * @return array
+ */
+ public function getPaymentMethods() {
+ return $this->paymentMethods;
+ }
+
+ /**
+ * Returns the available payment methods for selection.
+ *
+ * @return array<string>
+ */
+ public function getPaymentMethodSelection() {
+ $selection = array();
+ foreach ($this->objectTypes as $objectType) {
+ $selection[$objectType->objectType] = WCF::getLanguage()->get('wcf.payment.'.$objectType->objectType);
+ }
+
+ return $selection;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\payment\method;
+use wcf\system\request\LinkHandler;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * IPaymentMethod implementation for Paypal.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.method
+ * @category Community Framework
+ */
+class PaypalPaymentMethod extends AbstractPaymentMethod {
+ /**
+ * @see \wcf\system\payment\method\IPaymentMethod::supportsRecurringPayments()
+ */
+ public function supportsRecurringPayments() {
+ return true;
+ }
+
+ /**
+ * @see \wcf\system\payment\method\IPaymentMethod::getSupportedCurrencies()
+ */
+ public function getSupportedCurrencies() {
+ return array(
+ 'AUD', // Australian Dollar
+ 'BRL', // Brazilian Real
+ 'CAD', // Canadian Dollar
+ 'CZK', // Czech Koruna
+ 'DKK', // Danish Krone
+ 'EUR', // Euro
+ 'HKD', // Hong Kong Dollar
+ 'HUF', // Hungarian Forint
+ 'ILS', // Israeli New Sheqel
+ 'JPY', // Japanese Yen
+ 'MYR', // Malaysian Ringgit
+ 'MXN', // Mexican Peso
+ 'NOK', // Norwegian Krone
+ 'NZD', // New Zealand Dollar
+ 'PHP', // Philippine Peso
+ 'PLN', // Polish Zloty
+ 'GBP', // Pound Sterling
+ 'RUB', // Russian Ruble
+ 'SGD', // Singapore Dollar
+ 'SEK', // Swedish Krona
+ 'CHF', // Swiss Franc
+ 'TWD', // Taiwan New Dollar
+ 'THB', // Thai Baht
+ 'TRY', // Turkish Lira
+ 'USD' // U.S. Dollar
+ );
+ }
+
+ /**
+ * @see \wcf\system\payment\method\IPaymentMethod::getPurchaseButton()
+ */
+ public function getPurchaseButton($cost, $currency, $name, $token, $returnURL, $cancelReturnURL, $isRecurring = false, $subscriptionLength = 0, $subscriptionLengthUnit = '') {
+ if ($isRecurring) {
+ // subscribe button
+ return '<form method="post" action="https://www.paypal.com/cgi-bin/webscr">
+ <input type="hidden" name="a3" value="'.$cost.'" />
+ <input type="hidden" name="p3" value="'.$subscriptionLength.'" />
+ <input type="hidden" name="t3" value="'.$subscriptionLengthUnit.'" />
+ <input type="hidden" name="src" value="1" />
+ <input type="hidden" name="business" value="'.StringUtil::encodeHTML(PAYPAL_EMAIL_ADDRESS).'" />
+ <input type="hidden" name="cancel_return" value="'.StringUtil::encodeHTML($cancelReturnURL).'" />
+ <input type="hidden" name="charset" value="utf-8" />
+ <input type="hidden" name="cmd" value="_xclick-subscriptions" />
+ <input type="hidden" name="currency_code" value="'.$currency.'" />
+ <input type="hidden" name="custom" value="'.StringUtil::encodeHTML($token).'" />
+ <input type="hidden" name="email" value="'.StringUtil::encodeHTML(WCF::getUser()->email).'" />
+ <input type="hidden" name="item_name" value="'.StringUtil::encodeHTML($name).'" />
+ <input type="hidden" name="lc" value="'.strtoupper(WCF::getLanguage()->languageCode).'" />
+ <input type="hidden" name="no_note" value="1" />
+ <input type="hidden" name="no_shipping" value="1" />
+ <input type="hidden" name="notify_url" value="'.StringUtil::encodeHTML(LinkHandler::getInstance()->getLink('PaypalCallback', array('appendSession' => false))).'" />
+ <input type="hidden" name="quantity" value="1" />
+ <input type="hidden" name="return" value="'.StringUtil::encodeHTML($returnURL).'" />
+
+ <button class="small" type="submit">'.WCF::getLanguage()->get('wcf.payment.paypal.button.subscribe').'</button>
+ </form>';
+ }
+ else {
+ return '<form method="post" action="https://www.paypal.com/cgi-bin/webscr">
+ <input type="hidden" name="amount" value="'.$cost.'" />
+ <input type="hidden" name="business" value="'.StringUtil::encodeHTML(PAYPAL_EMAIL_ADDRESS).'" />
+ <input type="hidden" name="cancel_return" value="'.StringUtil::encodeHTML($cancelReturnURL).'" />
+ <input type="hidden" name="charset" value="utf-8" />
+ <input type="hidden" name="cmd" value="_xclick" />
+ <input type="hidden" name="currency_code" value="'.$currency.'" />
+ <input type="hidden" name="custom" value="'.StringUtil::encodeHTML($token).'" />
+ <input type="hidden" name="email" value="'.StringUtil::encodeHTML(WCF::getUser()->email).'" />
+ <input type="hidden" name="item_name" value="'.StringUtil::encodeHTML($name).'" />
+ <input type="hidden" name="lc" value="'.strtoupper(WCF::getLanguage()->languageCode).'" />
+ <input type="hidden" name="no_note" value="1" />
+ <input type="hidden" name="no_shipping" value="1" />
+ <input type="hidden" name="notify_url" value="'.StringUtil::encodeHTML(LinkHandler::getInstance()->getLink('PaypalCallback', array('appendSession' => false))).'" />
+ <input type="hidden" name="quantity" value="1" />
+ <input type="hidden" name="return" value="'.StringUtil::encodeHTML($returnURL).'" />
+
+ <button class="small" type="submit">'.WCF::getLanguage()->get('wcf.payment.paypal.button.purchase').'</button>
+ </form>';
+ }
+ }
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace wcf\system\payment\method;
+
+/**
+ * IPaymentMethod implementation for SofortUeberweisung.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.method
+ * @category Community Framework
+ */
+class SofortUeberweisungPaymentMethod extends AbstractPaymentMethod {
+ /**
+ * @see \wcf\system\payment\method\IPaymentMethod::getSupportedCurrencies()
+ */
+ public function getSupportedCurrencies() {
+ return array(
+ 'EUR' // Euro
+ );
+ }
+
+ /**
+ * @see \wcf\system\payment\method\IPaymentMethod::getPurchaseButton()
+ */
+ public function getPurchaseButton($cost, $currency, $name, $token, $returnURL, $cancelReturnURL, $isRecurring = false, $subscriptionLength = 0, $subscriptionLengthUnit = '') {
+ // @todo
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\payment\type;
+
+/**
+ * Abstract implementation of a payment type.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.type
+ * @category Community Framework
+ */
+abstract class AbstractPaymentType implements IPaymentType {}
\ No newline at end of file
--- /dev/null
+<?php
+namespace wcf\system\payment\type;
+
+/**
+ * Default interface for payment types.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.type
+ * @category Community Framework
+ */
+interface IPaymentType {
+ /**
+ * Processes the given transaction.
+ *
+ * @param integer $paymentMethodObjectTypeID
+ * @param string $token
+ * @param float $amount
+ * @param string $currency
+ * @param string $transactionID
+ * @param string $status
+ * @param array $transactionDetails
+ */
+ public function processTransaction($paymentMethodObjectTypeID, $token, $amount, $currency, $transactionID, $status, $transactionDetails);
+}
\ No newline at end of file
--- /dev/null
+<?php
+namespace wcf\system\payment\type;
+use wcf\data\paid\subscription\transaction\log\PaidSubscriptionTransactionLog;
+use wcf\data\paid\subscription\transaction\log\PaidSubscriptionTransactionLogAction;
+use wcf\data\paid\subscription\user\PaidSubscriptionUser;
+use wcf\data\paid\subscription\user\PaidSubscriptionUserAction;
+use wcf\data\paid\subscription\PaidSubscription;
+use wcf\data\user\User;
+use wcf\system\exception\SystemException;
+
+/**
+ * IPaymentType implementation for paid subscriptions.
+ *
+ * @author Marcel Werk
+ * @copyright 2001-2014 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @package com.woltlab.wcf
+ * @subpackage system.payment.method
+ * @category Community Framework
+ */
+class PaidSubscriptionPaymentType extends AbstractPaymentType {
+ /**
+ * @see wcf\system\payment\type\IPaymentType::processTransaction()
+ */
+ public function processTransaction($paymentMethodObjectTypeID, $token, $amount, $currency, $transactionID, $status, $transactionDetails) {
+ $userSubscription = $user = $subscription = null;
+ try {
+ $tokenParts = explode(':', $token);
+ if (count($tokenParts) != 2) {
+ throw new SystemException('invalid token');
+ }
+ list($userID, $subscriptionID) = $tokenParts;
+
+ // get user object
+ $user = new User(intval($userID));
+ if (!$user->userID) {
+ throw new SystemException('invalid user');
+ }
+
+ // get subscription object
+ $subscription = new PaidSubscription(intval($subscriptionID));
+ if (!$subscription->subscriptionID) {
+ throw new SystemException('invalid subscription');
+ }
+
+ // search for existing subscription
+ $userSubscription = PaidSubscriptionUser::getSubscriptionUser($subscription->subscriptionID, $user->userID);
+
+ // search log for transaction id
+ $logEntry = PaidSubscriptionTransactionLog::getLogByTransactionID($paymentMethodObjectTypeID, $transactionID);
+ if ($logEntry !== null) {
+ throw new SystemException('transaction already processed');
+ }
+
+ $logMessage = '';
+ if ($status == 'completed') {
+ // validate payment amout
+ if ($amount != $subscription->cost || $currency != $subscription->currency) {
+ throw new SystemException('invalid payment amount');
+ }
+
+ // active/extend subscription
+ if ($userSubscription === null) {
+ // create new subscription
+ $action = new PaidSubscriptionUserAction(array(), 'create', array(
+ 'user' => $user,
+ 'subscription' => $subscription
+ ));
+ $returnValues = $action->executeAction();
+ $userSubscription = $returnValues['returnValues'];
+ }
+ else {
+ // extend existing subscription
+ $action = new PaidSubscriptionUserAction(array($userSubscription), 'extend');
+ $action->executeAction();
+ }
+ $logMessage = 'payment completed';
+ }
+ if ($status == 'reversed') {
+ if ($userSubscription !== null) {
+ // revoke subscription
+ $action = new PaidSubscriptionUserAction(array($userSubscription), 'revoke');
+ $action->executeAction();
+ }
+ $logMessage = 'payment reversed';
+ }
+ if ($status == 'canceled_reversal') {
+ if ($userSubscription !== null) {
+ // restore subscription
+ $action = new PaidSubscriptionUserAction(array($userSubscription), 'restore');
+ $action->executeAction();
+ }
+ $logMessage = 'reversal canceled';
+ }
+
+ // log success
+ $action = new PaidSubscriptionTransactionLogAction(array(), 'create', array('data' => array(
+ 'subscriptionUserID' => $userSubscription->subscriptionUserID,
+ 'user' => $user->userID,
+ 'subscriptionID' => $subscription->subscriptionID,
+ 'paymentMethodObjectTypeID' => $paymentMethodObjectTypeID,
+ 'logTime' => TIME_NOW,
+ 'transactionID' => $transactionID,
+ 'logMessage' => $logMessage,
+ 'transactionDetails' => serialize($transactionDetails)
+ )));
+ $action->executeAction();
+ }
+ catch (SystemException $e) {
+ // log failure
+ $action = new PaidSubscriptionTransactionLogAction(array(), 'create', array('data' => array(
+ 'subscriptionUserID' => ($userSubscription !== null ? $userSubscription->subscriptionUserID : null),
+ 'user' => ($user !== null ? $user->userID : null),
+ 'subscriptionID' => ($subscription !== null ? $subscription->subscriptionID : null),
+ 'paymentMethodObjectTypeID' => $paymentMethodObjectTypeID,
+ 'logTime' => TIME_NOW,
+ 'transactionID' => $transactionID,
+ 'logMessage' => $e,
+ 'transactionDetails' => serialize($transactionDetails)
+ )));
+ $action->executeAction();
+ throw $e;
+ }
+ }
+}
\ No newline at end of file
#userNotificationItemList > .groupedNotificationItem p > a {
font-weight: bold;
}
+
+.paidSubscriptionTeaserList {
+ max-height: 74px;
+ overflow: hidden;
+
+ > li {
+ float: left;
+ margin-bottom: 7px;
+ padding-right: 7px;
+ white-space: nowrap;
+ width: 300px;
+ word-wrap: normal;
+
+ .containerHeadline {
+ > h3 {
+ text-overflow: ellipsis;
+ overflow: hidden;
+ }
+ }
+ }
+}
\ No newline at end of file
<item name="wcf.acp.group.copy.copyMembers.description"><![CDATA[Mitglieder der kopierten Benutzergruppe werden automatisch auch Mitglied der neuen Benutzergruppe sein.]]></item>
<item name="wcf.acp.group.copy.copyUserGroupOptions"><![CDATA[Berechtigungen kopieren]]></item>
<item name="wcf.acp.group.copy.copyUserGroupOptions.description"><![CDATA[Die neue Benutzergruppe wird die gleichen Berechtigungen besitzen wie die kopierte Benutzergruppe.]]></item>
+ <item name="wcf.acp.group.option.admin.paidSubscription.canManageSubscription"><![CDATA[Kann bezahlte Mitgliedschaften verwalten]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.menu.link.captcha.question.add"><![CDATA[Frage hinzufügen]]></item>
<item name="wcf.acp.menu.link.captcha.question.list"><![CDATA[Fragen auflisten]]></item>
<item name="wcf.acp.menu.link.log.authentication.failure"><![CDATA[Fehlgeschlagene Anmeldungen]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription"><![CDATA[Bezahlte Mitgliedschaften]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription.list"><![CDATA[Mitgliedschaften auflisten]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription.user.list"><![CDATA[Aktive Mitgliedschaften auflisten]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription.transactionLog.list"><![CDATA[Transaktionen auflisten]]></item>
</category>
<category name="wcf.acp.notice">
<item name="wcf.acp.option.gravatar_default_type.wavatar"><![CDATA[Wavatar]]></item>
<item name="wcf.acp.option.gravatar_default_type.monsterid"><![CDATA[Monster-ID]]></item>
<item name="wcf.acp.option.gravatar_default_type.retro"><![CDATA[Retro]]></item>
+ <item name="wcf.acp.option.category.general.payment"><![CDATA[Zahlungsoptionen]]></item>
+ <item name="wcf.acp.option.category.general.payment.description"><![CDATA[TODO]]></item>
+ <item name="wcf.acp.option.available_payment_methods"><![CDATA[Unterstützte Zahlungsmethoden]]></item>
+ <item name="wcf.acp.option.available_payment_methods.description"><![CDATA[TODO]]></item>
+ <item name="wcf.acp.option.paypal_email_address"><![CDATA[Paypal-E-Mail-Adresse]]></item>
+ <item name="wcf.acp.option.paypal_email_address.description"><![CDATA[TODO]]></item>
+ <item name="wcf.acp.option.module_paid_subscription"><![CDATA[Bezahlte Mitgliedschaften aktivieren]]></item>
+ <item name="wcf.acp.option.module_paid_subscription.description"><![CDATA[TODO]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.acp.package.validation.failed"><![CDATA[Das hochgeladene Paket kann nicht installiert werden, bitte beachten Sie das unten stehende Prüfungsergebnis.]]></item>
</category>
+ <category name="wcf.acp.paidSubscription">
+ <item name="wcf.acp.paidSubscription.list"><![CDATA[Bezahlte Mitgliedschaften]]></item>
+ <item name="wcf.acp.paidSubscription.add"><![CDATA[Mitgliedschaft hinzufügen]]></item>
+ <item name="wcf.acp.paidSubscription.edit"><![CDATA[Mitgliedschaft bearbeiten]]></item>
+ <item name="wcf.acp.paidSubscription.userGroups"><![CDATA[Benutzergruppen]]></item>
+ <item name="wcf.acp.paidSubscription.userGroups.description"><![CDATA[Käufer dieser Mitgliedschaft werden automatisch den ausgewählten Benutzergruppen hinzugefügt.]]></item>
+ <item name="wcf.acp.paidSubscription.cost"><![CDATA[Kosten]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLength.permanent"><![CDATA[Unbegrenzte Laufzeit]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLength"><![CDATA[Laufzeit]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLengthUnit.D"><![CDATA[Tag(e)]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLengthUnit.M"><![CDATA[Monat(e)]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLengthUnit.Y"><![CDATA[Jahr(e)]]></item>
+ <item name="wcf.acp.paidSubscription.showOrder"><![CDATA[Reihenfolge]]></item>
+ <item name="wcf.acp.paidSubscription.showOrder.description"><![CDATA[Legt die Reihenfolge fest, in der die bezahlten Mitgliedschaften angezeigt werden.]]></item>
+ <item name="wcf.acp.paidSubscription.isDisabled"><![CDATA[Mitgliedschaft deaktivieren]]></item>
+ <item name="wcf.acp.paidSubscription.isDisabled.description"><![CDATA[Entfernt diese bezahlte Mitgliedschaft aus dem Angebot.]]></item>
+ <item name="wcf.acp.paidSubscription.isRecurring"><![CDATA[Regelmäßige Abbuchung]]></item>
+ <item name="wcf.acp.paidSubscription.isRecurring.description"><![CDATA[Aktiviert die automatische Abbuchung der Kosten vom Konto des Käufers, um die Mitgliedschaft automatisch zu verlängern.]]></item>
+ <item name="wcf.acp.paidSubscription.paymentOptions"><![CDATA[Zahlungsoptionen]]></item>
+ <item name="wcf.acp.paidSubscription.user.list"><![CDATA[Aktive Mitgliedschaften]]></item>
+ <item name="wcf.acp.paidSubscription.user.endDate"><![CDATA[Ablaufdatum]]></item>
+ <item name="wcf.acp.paidSubscription.subscription"><![CDATA[Mitgliedschaft]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.list"><![CDATA[Transaktionen]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.paymentMethod"><![CDATA[Zahlungsmethode]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.transactionID"><![CDATA[Transaktions-ID]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.logTime"><![CDATA[Datum]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.logMessage"><![CDATA[Aktion]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog"><![CDATA[Transaktion]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.transactionDetails"><![CDATA[Details]]></item>
+ <item name="wcf.acp.paidSubscription.excludedSubscriptions"><![CDATA[Andere Mitgliedschaften ausschließen]]></item>
+ <item name="wcf.acp.paidSubscription.excludedSubscriptions.description"><![CDATA[Sie können den Erwerb anderer bezahlter Mitgliederschaften für Käufer dieser Mitgliedschaft ausschließen.]]></item>
+ <item name="wcf.acp.paidSubscription.user.delete.confirmMessage"><![CDATA[Wollen Sie die Mitgliedschaft „{$subscriptionUser->title|language}“ für den Benutzer „{$subscriptionUser->username}“ wirklich entfernen?]]></item>
+ <item name="wcf.acp.paidSubscription.user.add"><![CDATA[Mitgliedschaft manuell zuweisen]]></item>
+ </category>
+
<category name="wcf.acp.pageMenu">
<item name="wcf.acp.pageMenu.add"><![CDATA[Menüpunkt hinzufügen]]></item>
<item name="wcf.acp.pageMenu.advanced"><![CDATA[Erweitert]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.usersOnlineSidebar"><![CDATA[Benutzer online]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.followingsOnlineSidebar"><![CDATA[Benutzer online, denen Sie folgen]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.staffOnlineSidebar"><![CDATA[Team-Mitglieder online]]></item>
+ <item name="wcf.dashboard.box.com.woltlab.wcf.paidSubscriptions"><![CDATA[Bezahlte Mitgliedschaften]]></item>
+ <item name="wcf.dashboard.box.com.woltlab.wcf.paidSubscriptionsSidebar"><![CDATA[Bezahlte Mitgliedschaften]]></item>
</category>
<category name="wcf.date">
<item name="wcf.page.com.woltlab.wcf.UsersOnlineListPage"><![CDATA[Benutzer online]]></item>
</category>
+ <category name="wcf.paidSubscription">
+ <item name="wcf.paidSubscription.availableSubscriptions"><![CDATA[Verfügbare Mitgliedschaften]]></item>
+ <item name="wcf.paidSubscription.purchasedSubscriptions"><![CDATA[Erworbene Mitgliedschaften]]></item>
+ <item name="wcf.paidSubscription.formattedCost"><![CDATA[{$subscription->currency} {$subscription->cost|currency}{if $subscription->subscriptionLength} {if $subscription->subscriptionLength != 1}für{else}pro{/if} {if $subscription->subscriptionLength != 1}{@$subscription->subscriptionLength} {/if}{if $subscription->subscriptionLengthUnit == 'D'}Tag{if $subscription->subscriptionLength != 1}e{/if}{/if}{if $subscription->subscriptionLengthUnit == 'M'}Monat{if $subscription->subscriptionLength != 1}e{/if}{/if}{if $subscription->subscriptionLengthUnit == 'Y'}Jahr{if $subscription->subscriptionLength != 1}e{/if}{/if}{/if}]]></item>
+ <item name="wcf.paidSubscription.expires"><![CDATA[Gültig bis]]></item>
+ <item name="wcf.paidSubscription.returnMessage"><![CDATA[Danke für Ihre Zahlung. Ihre Transaktion wurde abgeschlossen. Sobald Ihre Zahlung von uns verarbeitet wurde, wird die erworbene Mitgliedschaft aktiviert.]]></item>
+ </category>
+
+ <category name="wcf.payment">
+ <item name="wcf.payment.com.woltlab.wcf.payment.method.paypal"><![CDATA[Paypal]]></item>
+ <item name="wcf.payment.paypal.button.purchase"><![CDATA[Via Paypal kaufen]]></item>
+ <item name="wcf.payment.paypal.button.subscribe"><![CDATA[Via Paypal abonnieren]]></item>
+ </category>
+
<category name="wcf.poll">
<item name="wcf.poll.button.addOption"><![CDATA[Weitere Antwort hinzufügen]]></item>
<item name="wcf.poll.button.removeOption"><![CDATA[Antwort entfernen]]></item>
<item name="wcf.user.menu.profile.signature"><![CDATA[Signatur]]></item>
<item name="wcf.user.menu.settings"><![CDATA[Einstellungen]]></item>
<item name="wcf.user.menu.settings.notification"><![CDATA[Benachrichtigungen]]></item>
+ <item name="wcf.user.menu.settings.paidSubscription"><![CDATA[Bezahlte Mitgliedschaften]]></item>
</category>
<category name="wcf.user.register">
<item name="wcf.acp.group.copy.copyMembers.description"><![CDATA[Members of the copied user group will automatically be members of the new user group.]]></item>
<item name="wcf.acp.group.copy.copyUserGroupOptions"><![CDATA[Copy Permissions]]></item>
<item name="wcf.acp.group.copy.copyUserGroupOptions.description"><![CDATA[The new user group will have the same permissions as the copied user group.]]></item>
+ <item name="wcf.acp.group.option.admin.paidSubscription.canManageSubscription"><![CDATA[TODO: Kann bezahlte Mitgliedschaften verwalten]]></item>
</category>
<category name="wcf.acp.index">
<item name="wcf.acp.menu.link.captcha.question.add"><![CDATA[Add Question]]></item>
<item name="wcf.acp.menu.link.captcha.question.list"><![CDATA[List Questions]]></item>
<item name="wcf.acp.menu.link.log.authentication.failure"><![CDATA[Failed Login Attempts]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription"><![CDATA[TODO: Bezahlte Mitgliedschaften]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription.list"><![CDATA[TODO: Mitgliedschaften auflisten]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription.user.list"><![CDATA[TODO: Aktive Mitgliedschaften auflisten]]></item>
+ <item name="wcf.acp.menu.link.paidSubscription.transactionLog.list"><![CDATA[TODO: Transaktionen auflisten]]></item>
</category>
<category name="wcf.acp.notice">
<item name="wcf.acp.option.gravatar_default_type.wavatar"><![CDATA[Wavatar]]></item>
<item name="wcf.acp.option.gravatar_default_type.monsterid"><![CDATA[Monster id]]></item>
<item name="wcf.acp.option.gravatar_default_type.retro"><![CDATA[Retro]]></item>
+ <item name="wcf.acp.option.category.general.payment"><![CDATA[TODO: Zahlungsoptionen]]></item>
+ <item name="wcf.acp.option.category.general.payment.description"><![CDATA[TODO]]></item>
+ <item name="wcf.acp.option.available_payment_methods"><![CDATA[TODO: Unterstützte Zahlungsmethoden]]></item>
+ <item name="wcf.acp.option.available_payment_methods.description"><![CDATA[TODO]]></item>
+ <item name="wcf.acp.option.paypal_email_address"><![CDATA[TODO: Paypal-E-Mail-Adresse]]></item>
+ <item name="wcf.acp.option.paypal_email_address.description"><![CDATA[TODO]]></item>
+ <item name="wcf.acp.option.module_paid_subscription"><![CDATA[TODO: Bezahlte Mitgliedschaften aktivieren]]></item>
+ <item name="wcf.acp.option.module_paid_subscription.description"><![CDATA[TODO]]></item>
</category>
<category name="wcf.acp.package">
<item name="wcf.acp.package.validation.failed"><![CDATA[The package cannot be installed, please review the validation results below.]]></item>
</category>
+ <category name="wcf.acp.paidSubscription">
+ <item name="wcf.acp.paidSubscription.list"><![CDATA[TODO: Bezahlte Mitgliedschaften]]></item>
+ <item name="wcf.acp.paidSubscription.add"><![CDATA[TODO: Mitgliedschaft hinzufügen]]></item>
+ <item name="wcf.acp.paidSubscription.edit"><![CDATA[TODO: Mitgliedschaft bearbeiten]]></item>
+ <item name="wcf.acp.paidSubscription.userGroups"><![CDATA[TODO: Benutzergruppen]]></item>
+ <item name="wcf.acp.paidSubscription.userGroups.description"><![CDATA[TODO: Käufer dieser Mitgliedschaft werden automatisch den ausgewählten Benutzergruppen hinzugefügt.]]></item>
+ <item name="wcf.acp.paidSubscription.cost"><![CDATA[TODO: Kosten]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLength.permanent"><![CDATA[TODO: Unbegrenzte Laufzeit]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLength"><![CDATA[TODO: Laufzeit]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLengthUnit.D"><![CDATA[TODO: Tag(e)]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLengthUnit.M"><![CDATA[TODO: Monat(e)]]></item>
+ <item name="wcf.acp.paidSubscription.subscriptionLengthUnit.Y"><![CDATA[TODO: Jahr(e)]]></item>
+ <item name="wcf.acp.paidSubscription.showOrder"><![CDATA[TODO: Reihenfolge]]></item>
+ <item name="wcf.acp.paidSubscription.showOrder.description"><![CDATA[TODO: Legt die Reihenfolge fest, in der die bezahlten Mitgliedschaften angezeigt werden.]]></item>
+ <item name="wcf.acp.paidSubscription.isDisabled"><![CDATA[TODO: Mitgliedschaft deaktivieren]]></item>
+ <item name="wcf.acp.paidSubscription.isDisabled.description"><![CDATA[TODO: Entfernt diese bezahlte Mitgliedschaft aus dem Angebot.]]></item>
+ <item name="wcf.acp.paidSubscription.isRecurring"><![CDATA[TODO: Regelmäßige Abbuchung]]></item>
+ <item name="wcf.acp.paidSubscription.isRecurring.description"><![CDATA[TODO: Aktiviert die automatische Abbuchung der Kosten vom Konto des Käufers, um die Mitgliedschaft automatisch zu verlängern.]]></item>
+ <item name="wcf.acp.paidSubscription.paymentOptions"><![CDATA[TODO: Zahlungsoptionen]]></item>
+ <item name="wcf.acp.paidSubscription.user.list"><![CDATA[TODO: Aktive Mitgliedschaften]]></item>
+ <item name="wcf.acp.paidSubscription.user.endDate"><![CDATA[TODO: Ablaufdatum]]></item>
+ <item name="wcf.acp.paidSubscription.subscription"><![CDATA[TODO: Mitgliedschaft]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.list"><![CDATA[TODO: Transaktionen]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.paymentMethod"><![CDATA[TODO: Zahlungsmethode]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.transactionID"><![CDATA[TODO: Transaktions-ID]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.logTime"><![CDATA[TODO: Datum]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.logMessage"><![CDATA[TODO: Aktion]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog"><![CDATA[TODO: Transaktion]]></item>
+ <item name="wcf.acp.paidSubscription.transactionLog.transactionDetails"><![CDATA[TODO: Details]]></item>
+ <item name="wcf.acp.paidSubscription.excludedSubscriptions"><![CDATA[TODO: Andere Mitgliedschaften ausschließen]]></item>
+ <item name="wcf.acp.paidSubscription.excludedSubscriptions.description"><![CDATA[TODO: Sie können den Erwerb anderer bezahlter Mitgliederschaften für Käufer dieser Mitgliedschaft ausschließen.]]></item>
+ <item name="wcf.acp.paidSubscription.user.delete.confirmMessage"><![CDATA[TODO: Wollen Sie die Mitgliedschaft „{$subscriptionUser->title|language}“ für den Benutzer „{$subscriptionUser->username}“ wirklich entfernen?]]></item>
+ <item name="wcf.acp.paidSubscription.user.add"><![CDATA[TODO: Mitgliedschaft manuell zuweisen]]></item>
+ </category>
+
<category name="wcf.acp.pageMenu">
<item name="wcf.acp.pageMenu.add"><![CDATA[Add Menu Item]]></item>
<item name="wcf.acp.pageMenu.advanced"><![CDATA[Advanced]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.usersOnlineSidebar"><![CDATA[Users Online]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.followingsOnlineSidebar"><![CDATA[Users Online you Follow]]></item>
<item name="wcf.dashboard.box.com.woltlab.wcf.user.staffOnlineSidebar"><![CDATA[Staff-Members Online]]></item>
+ <item name="wcf.dashboard.box.com.woltlab.wcf.paidSubscriptions"><![CDATA[TODO: Bezahlte Mitgliedschaften]]></item>
+ <item name="wcf.dashboard.box.com.woltlab.wcf.paidSubscriptionsSidebar"><![CDATA[TODO: Bezahlte Mitgliedschaften]]></item>
</category>
<category name="wcf.date">
<item name="wcf.page.com.woltlab.wcf.UsersOnlineListPage"><![CDATA[Users Online]]></item>
</category>
+ <category name="wcf.paidSubscription">
+ <item name="wcf.paidSubscription.availableSubscriptions"><![CDATA[TODO: Verfügbare Mitgliedschaften]]></item>
+ <item name="wcf.paidSubscription.purchasedSubscriptions"><![CDATA[TODO: Erworbene Mitgliedschaften]]></item>
+ <item name="wcf.paidSubscription.formattedCost"><![CDATA[TODO: {$subscription->currency} {$subscription->cost|currency}{if $subscription->subscriptionLength} {if $subscription->subscriptionLength != 1}für{else}pro{/if} {if $subscription->subscriptionLength != 1}{@$subscription->subscriptionLength} {/if}{if $subscription->subscriptionLengthUnit == 'D'}Tag{if $subscription->subscriptionLength != 1}e{/if}{/if}{if $subscription->subscriptionLengthUnit == 'M'}Monat{if $subscription->subscriptionLength != 1}e{/if}{/if}{if $subscription->subscriptionLengthUnit == 'Y'}Jahr{if $subscription->subscriptionLength != 1}e{/if}{/if}{/if}]]></item>
+ <item name="wcf.paidSubscription.expires"><![CDATA[TODO: Gültig bis]]></item>
+ <item name="wcf.paidSubscription.returnMessage"><![CDATA[TODO: Danke für Ihre Zahlung. Ihre Transaktion wurde abgeschlossen. Sobald Ihre Zahlung von uns verarbeitet wurde, wird die erworbene Mitgliedschaft aktiviert.]]></item>
+ </category>
+
+ <category name="wcf.payment">
+ <item name="wcf.payment.com.woltlab.wcf.payment.method.paypal"><![CDATA[TODO: Paypal]]></item>
+ <item name="wcf.payment.paypal.button.purchase"><![CDATA[TODO: Via Paypal kaufen]]></item>
+ <item name="wcf.payment.paypal.button.subscribe"><![CDATA[TODO: Via Paypal abonnieren]]></item>
+ </category>
+
<category name="wcf.poll">
<item name="wcf.poll.button.addOption"><![CDATA[Add Another Option]]></item>
<item name="wcf.poll.button.removeOption"><![CDATA[Remove Option]]></item>
<item name="wcf.user.menu.profile.signature"><![CDATA[Signature]]></item>
<item name="wcf.user.menu.settings"><![CDATA[Settings]]></item>
<item name="wcf.user.menu.settings.notification"><![CDATA[Notifications]]></item>
+ <item name="wcf.user.menu.settings.paidSubscription"><![CDATA[TODO: Bezahlte Mitgliedschaften]]></item>
</category>
<category name="wcf.user.register">
UNIQUE KEY (packageID, menuItem)
);
+DROP TABLE IF EXISTS wcf1_paid_subscription;
+CREATE TABLE wcf1_paid_subscription (
+ subscriptionID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ title VARCHAR(255) NOT NULL DEFAULT '',
+ description TEXT,
+ isDisabled TINYINT(1) NOT NULL DEFAULT 0,
+ showOrder INT(10) NOT NULL DEFAULT 0,
+ cost DECIMAL(10,2) NOT NULL DEFAULT 0,
+ currency VARCHAR(3) NOT NULL DEFAULT 'EUR',
+ subscriptionLength SMALLINT(3) NOT NULL DEFAULT 0,
+ subscriptionLengthUnit ENUM('', 'D', 'M', 'Y') NOT NULL DEFAULT '',
+ isRecurring TINYINT(1) NOT NULL DEFAULT 0,
+ groupIDs TEXT,
+ excludedSubscriptionIDs TEXT
+);
+
+DROP TABLE IF EXISTS wcf1_paid_subscription_user;
+CREATE TABLE wcf1_paid_subscription_user (
+ subscriptionUserID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ subscriptionID INT(10) NOT NULL,
+ userID INT(10) NOT NULL,
+ startDate INT(10) NOT NULL DEFAULT 0,
+ endDate INT(10) NOT NULL DEFAULT 0,
+ isActive TINYINT(1) NOT NULL DEFAULT 1,
+
+ UNIQUE KEY (subscriptionID, userID),
+ KEY (isActive)
+);
+
+DROP TABLE IF EXISTS wcf1_paid_subscription_transaction_log;
+CREATE TABLE wcf1_paid_subscription_transaction_log (
+ logID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
+ subscriptionUserID INT(10),
+ userID INT(10),
+ subscriptionID INT(10),
+ paymentMethodObjectTypeID INT(10) NOT NULL,
+ logTime INT(10) NOT NULL DEFAULT 0,
+ transactionID VARCHAR(255) NOT NULL DEFAULT '',
+ transactionDetails MEDIUMTEXT,
+ logMessage VARCHAR(255) NOT NULL DEFAULT ''
+);
+
DROP TABLE IF EXISTS wcf1_poll;
CREATE TABLE wcf1_poll (
pollID INT(10) NOT NULL AUTO_INCREMENT PRIMARY KEY,
ALTER TABLE wcf1_package_update_version ADD FOREIGN KEY (packageUpdateID) REFERENCES wcf1_package_update (packageUpdateID) ON DELETE CASCADE;
+ALTER TABLE wcf1_paid_subscription_user ADD FOREIGN KEY (subscriptionID) REFERENCES wcf1_paid_subscription (subscriptionID) ON DELETE CASCADE;
+ALTER TABLE wcf1_paid_subscription_user ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;
+
+ALTER TABLE wcf1_paid_subscription_transaction_log ADD FOREIGN KEY (subscriptionUserID) REFERENCES wcf1_paid_subscription_user (subscriptionUserID) ON DELETE SET NULL;
+ALTER TABLE wcf1_paid_subscription_transaction_log ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE SET NULL;
+ALTER TABLE wcf1_paid_subscription_transaction_log ADD FOREIGN KEY (subscriptionID) REFERENCES wcf1_paid_subscription (subscriptionID) ON DELETE SET NULL;
+ALTER TABLE wcf1_paid_subscription_transaction_log ADD FOREIGN KEY (paymentMethodObjectTypeID) REFERENCES wcf1_object_type (objectTypeID) ON DELETE CASCADE;
+
ALTER TABLE wcf1_page_menu_item ADD FOREIGN KEY (packageID) REFERENCES wcf1_package (packageID) ON DELETE CASCADE;
ALTER TABLE wcf1_search ADD FOREIGN KEY (userID) REFERENCES wcf1_user (userID) ON DELETE CASCADE;