}
});
+/**
+ * Namespace for classes related to the WoltLab Plugin-Store.
+ */
+WCF.ACP.PluginStore = { };
+
+/**
+ * Namespace for classes handling items purchased in the WoltLab Plugin-Store.
+ */
+WCF.ACP.PluginStore.PurchasedItems = { };
+
+/**
+ * Searches for purchased items available for install but not yet installed.
+ */
+WCF.ACP.PluginStore.PurchasedItems.Search = Class.extend({
+ _dialog: null,
+ _proxy: null,
+ _wcfMajorReleases: [ ],
+
+ init: function(wcfMajorReleases) {
+ this._dialog = null;
+ this._proxy = new WCF.Action.Proxy({
+ success: $.proxy(this._success, this)
+ });
+ this._wcfMajorReleases = wcfMajorReleases;
+ if (!this._wcfMajorReleases.length) {
+ console.debug("[WCF.ACP.PluginStore.PurchasedItems.Search] No suitable WCF major releases found, aborting.");
+ return;
+ }
+
+ var $button = $('<li><a class="button"><span class="icon icon16 fa-shopping-cart" /> <span>' + WCF.Language.get('wcf.acp.pluginstore.purchasedItems.button.search') + '</span></a></li>');
+ $button.prependTo($('.contentNavigation:eq(0) > nav > ul')).click($.proxy(this._click, this));
+ },
+
+ _click: function() {
+ this._proxy.setOption('data', {
+ actionName: 'searchForPurchasedItems',
+ className: 'wcf\\data\\package\\PackageAction',
+ parameters: {
+ wcfMajorReleases: this._wcfMajorReleases
+ }
+ });
+ this._proxy.sendRequest();
+ },
+
+ _success: function(data, textStatus, jqXHR) {
+ if (data.returnValues.template) {
+ if (this._dialog === null) {
+ this._dialog = $('<div />').hide().appendTo(document.body);
+ this._dialog.html(data.returnValues.template).wcfDialog({
+ title: WCF.Language.get('wcf.acp.pluginstore.authorization')
+ });
+ }
+ else {
+ this._dialog.html(data.returnValues.template);
+ this._dialog.wcfDialog('open');
+ }
+
+ this._dialog.find('button').click($.proxy(this._submit, this));
+ }
+ else if (data.returnValues.noResults) {
+ this._dialog.wcfDialog('option', 'title', 'Gekaufte Produkte (Plugin-Store)');
+ this._dialog.html(data.returnValues.noResults);
+ this._dialog.wcfDialog('open');
+ }
+ },
+
+ _submit: function() {
+ this._dialog.wcfDialog('close');
+
+ this._proxy.setOption('data', {
+ actionName: 'searchForPurchasedItems',
+ className: 'wcf\\data\\package\\PackageAction',
+ parameters: {
+ password: $('#pluginStorePassword').val(),
+ username: $('#pluginStoreUsername').val(),
+ wcfMajorReleases: this._wcfMajorReleases
+ }
+ });
+ this._proxy.sendRequest();
+ }
+});
+
/**
* Handles option selection.
*/
WCF.Language.addObject({
'wcf.acp.package.searchForUpdates': '{lang}wcf.acp.package.searchForUpdates{/lang}',
'wcf.acp.package.searchForUpdates.noResults': '{lang}wcf.acp.package.searchForUpdates.noResults{/lang}',
- 'wcf.acp.package.uninstallation.title': '{lang}wcf.acp.package.uninstallation.title{/lang}'
+ 'wcf.acp.package.uninstallation.title': '{lang}wcf.acp.package.uninstallation.title{/lang}',
+ 'wcf.acp.pluginstore.authorization': '{lang}wcf.acp.pluginstore.authorization{/lang}',
+ 'wcf.acp.pluginstore.purchasedItems': '{lang}wcf.acp.pluginstore.purchasedItems{/lang}',
+ 'wcf.acp.pluginstore.purchasedItems.button.search': '{lang}wcf.acp.pluginstore.purchasedItems.button.search{/lang}',
+ 'wcf.acp.pluginstore.purchasedItems.noResults': '{lang}wcf.acp.pluginstore.purchasedItems.noResults{/lang}'
});
{if $__wcf->session->getPermission('admin.system.package.canUninstallPackage')}
{if $__wcf->session->getPermission('admin.system.package.canUpdatePackage')}
new WCF.ACP.Package.Update.Search();
{/if}
+
+ new WCF.ACP.PluginStore.PurchasedItems.Search([ {implode from=$wcfMajorReleases item=wcfMajorRelease}'{$wcfMajorRelease}'{/implode} ]);
});
//]]>
</script>
--- /dev/null
+{if $rejected}
+ <p class="error">{lang}wcf.acp.pluginstore.authorization.credentails.rejected{/lang}</p>
+{/if}
+
+<fieldset{if $rejected} class="marginTop"{/if}>
+ <legend>{lang}wcf.acp.pluginstore.authorization.credentials{/lang}</legend>
+ <small>{lang}wcf.acp.pluginstore.authorization.credentails.description{/lang}</small>
+
+ <dl>
+ <dt><label for="pluginStoreUsername">{lang}wcf.acp.pluginstore.authorization.username{/lang}</label></dt>
+ <dd><input type="text" id="pluginStoreUsername" value="" class="long" /></dd>
+ </dl>
+
+ <dl>
+ <dt><label for="pluginStorePassword">{lang}wcf.acp.pluginstore.authorization.password{/lang}</label></dt>
+ <dd><input type="password" id="pluginStorePassword" value="" class="long" /></dd>
+ </dl>
+</fieldset>
+
+<div class="formSubmit">
+ <button>{lang}wcf.global.button.submit{/lang}</button>
+</div>
\ No newline at end of file
namespace wcf\acp\page;
use wcf\page\SortablePage;
use wcf\system\WCF;
+use wcf\data\package\update\server\PackageUpdateServer;
/**
* Shows a list of all installed packages.
*/
public $objectListClassName = 'wcf\data\package\PackageList';
+ /**
+ * list of WCF major releases covered by existing store update servers
+ * @var array<string>
+ */
+ public $wcfMajorReleases = array();
+
/**
* @see \wcf\page\IPage::readParameters()
*/
if (isset($_GET['packageID'])) $this->packageID = intval($_GET['packageID']);
}
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ $updateServers = PackageUpdateServer::getActiveUpdateServers();
+ foreach ($updateServers as $updateServer) {
+ if (preg_match('~^https?://store.woltlab.com/(maelstrom|typhoon)/$~', $updateServer->serverURL, $matches)) {
+ $this->wcfMajorReleases[] = $matches[1];
+ }
+ }
+ }
+
/**
* @see \wcf\page\IPage::assignVariables()
*/
parent::assignVariables();
WCF::getTPL()->assign(array(
- 'packageID' => $this->packageID
+ 'packageID' => $this->packageID,
+ 'wcfMajorReleases' => $this->wcfMajorReleases
));
}
--- /dev/null
+<?php
+namespace wcf\acp\page;
+use wcf\page\AbstractPage;
+use wcf\system\exception\IllegalLinkException;
+use wcf\system\WCF;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+
+/**
+ * Shows a list of purchased plugin store items.
+ *
+ * @author Alexander Ebert
+ * @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 PluginStorePurchasedItemsPage extends AbstractPage {
+ /**
+ * @see \wcf\page\AbstractPage::$activeMenuItem
+ */
+ public $activeMenuItem = 'wcf.acp.menu.link.package';
+
+ /**
+ * @see \wcf\page\AbstractPage::$neededPermissions
+ */
+ public $neededPermissions = array('admin.system.package.canUpdatePackage', 'admin.system.package.canUninstallPackage');
+
+ /**
+ * list of purchased products grouped by WCF major release.
+ * @var array<array>
+ */
+ public $products = array();
+
+ /**
+ * @see \wcf\page\IPage::readParameters()
+ */
+ public function readParameters() {
+ parent::readParameters();
+
+ $this->products = WCF::getSession()->getVar('__pluginStoreProducts');
+ if (empty($this->products)) {
+ throw new IllegalLinkException();
+ }
+
+ die("<pre>".print_r($this->products, true));
+ }
+
+ /**
+ * @see \wcf\page\IPage::readData()
+ */
+ public function readData() {
+ parent::readData();
+
+ $availableProducts = array();
+ foreach ($this->products as $products) {
+ $availableProducts = array_merge($availableProducts, array_keys($products));
+ }
+
+ $conditions = new PreparedStatementConditionBuilder();
+ $conditions->add("package IN (?)")
+ }
+
+ /**
+ * @see \wcf\page\IPage::assignVariables()
+ */
+ public function assignVariables() {
+ parent::assignVariables();
+
+ WCF::getTPL()->assign(array(
+ 'products' => $this->products
+ ));
+ }
+}
<?php
namespace wcf\data\package;
use wcf\data\AbstractDatabaseObjectAction;
+use wcf\system\exception\HTTPUnauthorizedException;
+use wcf\system\exception\UserInputException;
+use wcf\system\WCF;
+use wcf\util\HTTPRequest;
+use wcf\util\JSON;
+use wcf\system\exception\SystemException;
+use wcf\system\request\LinkHandler;
/**
* Executes package-related actions.
* @see \wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
*/
protected $permissionsUpdate = array('admin.system.package.canUpdatePackage');
+
+ public function validateSearchForPurchasedItems() {
+ // TODO: validate permissions
+
+ $this->readString('password', true);
+ $this->readString('username', true);
+
+ if (empty($this->parameters['wcfMajorReleases']) || !is_array($this->parameters['wcfMajorReleases'])) {
+ throw new UserInputException('wcfMajorReleases');
+ }
+
+ if (empty($this->parameters['username'])) {
+ // check if user has already provided credentials
+ $sql = "SELECT loginUsername, loginPassword
+ FROM wcf".WCF_N."_package_update_server
+ WHERE serverURL = ?";
+ $statement = WCF::getDB()->prepareStatement($sql, 1);
+ $statement->execute(array('http://store.woltlab.com/typhoon/'));
+ $row = $statement->fetchArray();
+ if (!empty($row['loginUsername']) && !empty($row['loginPassword'])) {
+ $this->parameters['password'] = $row['loginPassword'];
+ $this->parameters['username'] = $row['loginUsername'];
+ }
+ }
+ }
+
+ public function searchForPurchasedItems() {
+ if (empty($this->parameters['username']) || empty($this->parameters['password'])) {
+ return array(
+ 'template' => $this->renderAuthorizationDialog(false)
+ );
+ }
+
+ $request = new HTTPRequest('https://www.woltlab.com/api/1.0/customer/purchases/list.json', array(
+ 'method' => 'POST'
+ ), array(
+ 'username' => $this->parameters['username'],
+ 'password' => $this->parameters['password'],
+ 'wcfMajorReleases' => $this->parameters['wcfMajorReleases']
+ ));
+
+ $request->execute();
+ $reply = $request->getReply();
+ $response = JSON::decode($reply['body']);
+
+ $code = (isset($response['status'])) ? $response['status'] : 500;
+ switch ($code) {
+ case 200:
+ if (empty($response['products'])) {
+ return array(
+ 'noResults' => WCF::getLanguage()->get('wcf.acp.pluginstore.purchasedItems.noResults')
+ );
+ }
+ else {
+ WCF::getSession()->register('__pluginStoreProducts', $response['products']);
+
+ return array(
+ 'redirectURL' => LinkHandler::getInstance()->getLink('PluginStorePurchasedItems')
+ );
+ }
+ break;
+
+ // authentication error
+ case 401:
+ return array(
+ 'template' => $this->renderAuthorizationDialog(true)
+ );
+ break;
+
+ // any other kind of errors
+ default:
+ throw new SystemException(WCF::getLanguage()->getDynamicVariable('wcf.acp.pluginstore.api.error', array('status' => $code)));
+ break;
+ }
+ }
+
+ protected function renderAuthorizationDialog($rejected) {
+ WCF::getTPL()->assign(array(
+ 'rejected' => $rejected
+ ));
+
+ return WCF::getTPL()->fetch('pluginStoreAuthorization');
+ }
}
$data = json_decode($json, $asArray);
if ($data === null && self::getLastError() !== JSON_ERROR_NONE) {
- throw new SystemException('Could not decode JSON "'.$json.'"');
+ throw new SystemException('Could not decode JSON (error '.self::getLastError().'): '.$json);
}
return $data;
<item name="wcf.acp.pageMenu.menuItemParameters"><![CDATA[Parameter]]></item>
</category>
+ <category name="wcf.acp.pluginstore">
+ <item name="wcf.acp.pluginstore.api.error"><![CDATA[Fehler {@$status}): Der Server konnte die Anfrage nicht erfolgreich bearbeiten.]]></item>
+ <item name="wcf.acp.pluginstore.authorization"><![CDATA[Autorisierung erforderlich]]></item>
+ <item name="wcf.acp.pluginstore.authorization.credentials"><![CDATA[Zugangsdaten]]></item>
+ <item name="wcf.acp.pluginstore.authorization.credentials.description"><![CDATA[Geben Sie bitte Benutzername und Passwort für Ihr WoltLab.com-Benutzerkonto an.]]></item>
+ <item name="wcf.acp.pluginstore.authorization.credentials.rejected"><![CDATA[Ihre Zugangsdaten wurden vom Server abgewiesen, bitte überprüfen Sie Benutzername und Passwort!]]></item>
+ <item name="wcf.acp.pluginstore.authorization.username"><![CDATA[Benutzername]]></item>
+ <item name="wcf.acp.pluginstore.authorization.password"><![CDATA[Password]]></item>
+ <item name="wcf.acp.pluginstore.authorization.saveCredentials"><![CDATA[Zugangsdaten für aktuelle Sitzung speichern]]></item>
+ <item name="wcf.acp.pluginstore.purchasedItems.button.search"><![CDATA[Gekaufte Produkte (Plugin-Store)]]></item>
+ <item name="wcf.acp.pluginstore.purchasedItems"><![CDATA[Gekaufte Produkte (Plugin-Store)]]></item>
+ <item name="wcf.acp.pluginstore.purchasedItems.noResults"><![CDATA[Die Suche ergab keine Treffer, entweder haben Sie noch keine Produkte erworben oder diese sind nicht kompatibel.]]></item>
+ </category>
+
<category name="wcf.acp.rebuildData">
<item name="wcf.acp.rebuildData"><![CDATA[Anzeigen aktualisieren]]></item>
<item name="wcf.acp.rebuildData.description"><![CDATA[Für eine vollständige Aktualisierung aller Daten (z.B. nach einem Datenimport) führen Sie die Aktionen bitte in der hier angegebenen Reihenfolge durch.]]></item>