From: Alexander Ebert Date: Tue, 13 Sep 2011 19:06:34 +0000 (+0200) Subject: Implemented worker system based upon AJAX dialogs. X-Git-Tag: 2.0.0_Beta_1~1765^2~16^2 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=d61503f988a201415525e2b97933f65165ad044b;p=GitHub%2FWoltLab%2FWCF.git Implemented worker system based upon AJAX dialogs. Reworked the previous worker system relying on the old-style WCF 1.1 worker system as used by the installation itself. Right now the system is pretty unstable and I'm not sure wether the loop calculation is right. --- diff --git a/wcfsetup/install/files/acp/js/WCF.ACP.js b/wcfsetup/install/files/acp/js/WCF.ACP.js index ecb5779700..f09fda84c7 100644 --- a/wcfsetup/install/files/acp/js/WCF.ACP.js +++ b/wcfsetup/install/files/acp/js/WCF.ACP.js @@ -530,4 +530,89 @@ WCF.ACP.User.List.prototype = { WCF.Clipboard.sendRequest(item); } } +}; + +/** + * Worker support for ACP. + * + * @param string dialogID + * @param string className + * @param object options + */ +WCF.ACP.Worker = function(dialogID, className, options) { this.init(dialogID, className, options); }; +WCF.ACP.Worker.prototype = { + /** + * dialog id + * @var string + */ + _dialogID: null, + + /** + * dialog object + * @var jQuery + */ + _dialog: null, + + /** + * Initializes a new worker instance. + * + * @param string dialogID + * @param string className + * @param object options + */ + init: function(dialogID, className, options) { + this._dialogID = dialogID + 'Worker'; + options = options || { }; + + // initialize AJAX-based dialog + WCF.showAJAXDialog(this._dialogID, true, { + ajax: { + url: 'index.php?action=WorkerProxy&t=' + SECURITY_TOKEN + SID_ARG_2ND, + type: 'POST', + data: { + className: className, + parameters: options + }, + success: $.proxy(this._handleResponse, this) + }, + preventClose: true, + hideTitle: true + }); + }, + + /** + * Handles response from server. + */ + _handleResponse: function() { + // init binding + if (this._dialog === null) { + this._dialog = $('#' + $.wcfEscapeID(this._dialogID)); + } + + // fetch data returned by server response + var $data = this._dialog.data('responseData'); + + // update progress + this._dialog.find('#workerProgress').attr('value', $data.progress).text($data.progress + '%'); + + // worker is still busy with it's business, carry on + if ($data.progress < 100) { + // send request for next loop + $.ajax({ + url: 'index.php?action=WorkerProxy&t=' + SECURITY_TOKEN + SID_ARG_2ND, + type: 'POST', + data: { + className: $data.className, + loopCount: $data.loopCount + }, + success: $.proxy(function(data) { + this._dialog.data('responseData', data); + this._handleResponse(); + }, this), + error: function(transport) { + alert(transport.responseText); + } + }); + } + } }; \ No newline at end of file diff --git a/wcfsetup/install/files/lib/acp/action/WorkerProxyAction.class.php b/wcfsetup/install/files/lib/acp/action/WorkerProxyAction.class.php new file mode 100644 index 0000000000..3bb3f904ad --- /dev/null +++ b/wcfsetup/install/files/lib/acp/action/WorkerProxyAction.class.php @@ -0,0 +1,142 @@ + + * @package com.woltlab.wcf + * @subpackage acp.action + * @category Community Framework + */ +class WorkerProxyAction extends AbstractSecureAction { + /** + * worker class name + * @var string + */ + protected $className = ''; + + /** + * loop counter + * @var integer + */ + protected $loopCount = 0; + + /** + * parameters for worker action + * @var array + */ + protected $parameters = array(); + + /** + * worker object + * @var wcf\system\worker\IWorker + */ + protected $worker = null; + + /** + * @see wcf\action\AbstractAction::_construct() + */ + public function __construct() { + try { + parent::__construct(); + } + catch (\Exception $e) { + if ($e instanceof AJAXException) { + throw $e; + } + else { + throw new AJAXException($e->getMessage()); + } + } + } + + /** + * @see wcf\action\IAction::readParameters() + */ + public function readParameters() { + parent::readParameters(); + + if (isset($_POST['className'])) $this->className = $_POST['className']; + if (isset($_POST['loopCount'])) $this->loopCount = intval($_POST['loopCount']); + if (isset($_POST['parameters']) && is_array($_POST['parameters'])) $this->parameters = $_POST['parameters']; + + $this->validate(); + } + + /** + * Validates class name. + */ + protected function validate() { + if (empty($this->className)) { + throw new SystemException("class name cannot be empty."); + } + + if (!ClassUtil::isInstanceOf($this->className, 'wcf\system\worker\IWorker')) { + throw new SystemException("class '".$this->className."' should implement the interface 'wcf\system\worker\IWorker'"); + } + } + + /** + * @see wcf\action\IAction::execute() + */ + public function execute() { + parent::execute(); + + // initialize worker + if ($this->loopCount == 0) { + $this->sendResponse(); + } + + $this->worker = new $this->className($this->parameters); + $this->worker->setLoopCount($this->loopCount); + $this->worker->validate(); + $returnValues = array(); + + $progress = $this->worker->getProgress(); + $returnValues['progress'] = $progress; + if ($progress < 100) { + $this->worker->execute(); + } + + // send current state + $this->sendResponse($progress, $this->worker->getParameters()); + + } + + /** + * Sends a JSON-encoded response. + * + * @param integer $progress + * @param array $parameters + */ + protected function sendResponse($progress = 0, array $parameters = null) { + if ($parameters === null) $parameters = $this->parameters; + + // build return values + $returnValues = array( + 'className' => $this->className, + 'loopCount' => $this->loopCount, + 'parameters' => $parameters, + 'progress' => $progress + ); + + // include template on startup + if ($progress == 0) { + $returnValues['template'] = WCF::getTPL()->fetch('worker'); + } + + // send JSON-encoded response + header('Content-type: application/json'); + echo JSON::encode($returnValues); + exit; + } +} diff --git a/wcfsetup/install/files/lib/system/worker/AbstractWorker.class.php b/wcfsetup/install/files/lib/system/worker/AbstractWorker.class.php new file mode 100644 index 0000000000..7c65c8a7dc --- /dev/null +++ b/wcfsetup/install/files/lib/system/worker/AbstractWorker.class.php @@ -0,0 +1,77 @@ + + * @package com.woltlab.wcf + * @subpackage system.worker + * @category Community Framework + */ +abstract class AbstractWorker implements IWorker { + /** + * count of total actions (limited by $limit per loop) + * @var integer + */ + protected $count = 0; + + /** + * limit of actions per loop + * @var integer + */ + protected $limit = 0; + + /** + * current loop count + * @var integer + */ + protected $loopCount = 0; + + /** + * list of additional parameters + * @var array + */ + protected $parameters = array(); + + /** + * @see wcf\system\worker\IWorker::__construct() + */ + public function __construct(array $parameters) { + $this->parameters = $parameters; + } + + /** + * @see wcf\system\worker\IWorker::getLoopCount() + */ + public function setLoopCount($loopCount) { + $this->loopCount = $loopCount; + } + + /** + * Counts objects applicable for worker action. + */ + abstract protected function countObjects(); + + /** + * @see wcf\system\worker\IWorker::getProgress() + */ + public function getProgress() { + $this->countObjects(); + + if (!$this->count) { + return 100; + } + + return ceil(($this->loopCount / ceil($this->count / $this->limit)) * 100); + } + + /** + * @see wcf\system\worker\IWorker::getParameters() + */ + public function getParameters() { + return $this->parameters; + } +} diff --git a/wcfsetup/install/files/lib/system/worker/IWorker.class.php b/wcfsetup/install/files/lib/system/worker/IWorker.class.php new file mode 100644 index 0000000000..b30c396b13 --- /dev/null +++ b/wcfsetup/install/files/lib/system/worker/IWorker.class.php @@ -0,0 +1,53 @@ + + * @package com.woltlab.wcf + * @subpackage system.worker + * @category Community Framework + */ +interface IWorker { + /** + * Creates a new worker object with additional parameters + * + * @param array $parameters + */ + public function __construct(array $parameters); + + /** + * Sets current loop count. + * + * @param integer $loopCount + */ + public function setLoopCount($loopCount); + + /** + * Gets current process, integer between 0 and 100. If the progress + * hits 100 the worker will terminate. + * + * @return integer + */ + public function getProgress(); + + /** + * Executes worker action. + */ + public function execute(); + + /** + * Returns parameters previously given within __construct(). + * + * @return array + */ + public function getParameters(); + + /** + * Validates parameters. + */ + public function validate(); +} diff --git a/wcfsetup/install/files/lib/system/worker/MailWorker.class.php b/wcfsetup/install/files/lib/system/worker/MailWorker.class.php new file mode 100644 index 0000000000..1a2f11430a --- /dev/null +++ b/wcfsetup/install/files/lib/system/worker/MailWorker.class.php @@ -0,0 +1,134 @@ + + * @package com.woltlab.wcf + * @subpackage system.worker + * @category Community Framework + */ +class MailWorker extends AbstractWorker { + /** + * condition builder object + * @var wcf\system\database\util\PreparedStatementConditionBuilder + */ + protected $conditions = null; + + /** + * @see wcf\system\worker\AbstractWorker::$limit + */ + protected $limit = 50; + + /** + * mail data + * @var array + */ + protected $mailData = null; + + /** + * @see wcf\system\worker\IWorker::validate() + */ + public function validate() { + WCF::getSession()->checkPermission('admin.user.canMailUser'); + + if (!isset($this->parameters['mailID'])) { + throw new SystemException("mailID missing"); + } + + $userMailData = WCF::getSession()->getVar('userMailData'); + if (!isset($userMailData[$this->parameters['mailID']])) { + throw new SystemException("mailID '" . $this->parameters['mailID'] . "' is invalid"); + } + + $this->mailData = $userMailData[$this->parameters['mailID']]; + } + + /** + * @see wcf\system\worker\IWorker::countObjects() + */ + public function countObjects() { + $this->conditions = new PreparedStatementConditionBuilder(); + if ($this->mailData['action'] == '') { + $this->conditions->add("user.userID IN (?)", array($this->mailData['userIDs'])); + } + else if ($this->mailData['action'] == 'group') { + $this->conditions->add("user.userID IN (SELECT userID FROM wcf".WCF_N."_user_to_group WHERE groupID IN (?))", array($this->mailData['groupIDs'])); + } + + $sql = "SELECT COUNT(*) AS count + FROM wcf".WCF_N."_user user + ".$this->conditions; + $statement = WCF::getDB()->prepareStatement($sql); + $statement->execute($this->conditions->getParameters()); + $row = $statement->fetchArray(); + + $this->count = $row['count']; + } + + /** + * @see wcf\system\worker\IWorker::getProgress() + */ + public function getProgress() { + $progress = parent::getProgress(); + + if ($progress == 100) { + // clear markings + $typeID = ClipboardHandler::getInstance()->getTypeID('com.woltlab.wcf.user'); + ClipboardHandler::getInstance()->removeItems($typeID); + + // clear session + $userMailData = WCF::getSession()->getVar('userMailData'); + unset($userMailData[$this->parameters['mailID']]); + WCF::getSession()->register('userMailData', $userMailData); + } + + return $progress; + } + + /** + * @see wcf\system\worker\IWorker::execute() + */ + public function execute() { + // get users + $sql = "SELECT user_option.*, user.* + FROM wcf".WCF_N."_user user + LEFT JOIN wcf".WCF_N."_user_option_value user_option + ON (user_option.userID = user.userID) + ".$this->conditions." + ORDER BY user.userID"; + $statement = WCF::getDB()->prepareStatement($sql, $this->limit, ($this->limit * $this->loopCount)); + $statement->execute($this->conditions->getParameters()); + while ($row = $statement->fetchArray()) { + $user = new User(null, $row); + $adminCanMail = $user->adminCanMail; + if ($adminCanMail === null || $adminCanMail) { + $this->sendMail($user); + } + } + } + + /** + * Sends the mail to given user. + * + * @param wcf\data\user\User $user + */ + protected function sendMail(User $user) { + // send mail + try { + $mail = new Mail(array($user->username => $user->email), $this->userMailData['subject'], StringUtil::replace('{$username}', $user->username, $this->mailData['text']), $this->mailData['from']); + if ($this->mailData['enableHTML']) $mail->setContentType('text/html'); + $mail->send(); + } + catch (SystemException $e) {} // ignore errors + } +}