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.
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
--- /dev/null
+<?php
+namespace wcf\acp\action;
+use wcf\action\AbstractSecureAction;
+use wcf\system\exception\AJAXException;
+use wcf\system\exception\SystemException;
+use wcf\system\WCF;
+use wcf\util\ClassUtil;
+use wcf\util\JSON;
+
+/**
+ * Handles worker actions.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\worker;
+
+/**
+ * Basic implementation for workers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @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;
+ }
+}
--- /dev/null
+<?php
+namespace wcf\system\worker;
+
+/**
+ * Basic interface for workers.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @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();
+}
--- /dev/null
+<?php
+namespace wcf\system\worker;
+use wcf\system\clipboard\ClipboardHandler;
+use wcf\system\database\util\PreparedStatementConditionBuilder;
+use wcf\system\exception\SystemException;
+use wcf\system\mail\Mail;
+use wcf\system\WCF;
+use wcf\util\StringUtil;
+
+/**
+ * Worker implementation for sending mails.
+ *
+ * @author Alexander Ebert
+ * @copyright 2001-2011 WoltLab GmbH
+ * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
+ * @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
+ }
+}