This article is primarly intended for developers, describing some techniques behind a new feature of our upcoming version of Community Framework.
Another goal of Community Framework 2.0 was to create an abstract and more straight-forward system to execute recurring actions through a known interface. You might have already stumbled upon
AbstractDatabaseObjectAction which is the default implementation for action processors and is usually expected by
AJAXProxyAction. Basically it splits up into two different approaches, whereas we have the typical access from PHP on the one hand and the new AJAX calls on the other.
Each action class consists of three steps until an action may be successfully completed:
- __construct(): initialize the whole object and allow event listeners to modify all data before we’re actually working with it
- validateAction(): performs defined pre-execution against, e.g. validating permissions and parameters
- executeAction(): actually does the whole job by executing the action and providing the return values
If you remember the usual validation within those
*Form-classes, you might wonder what’s the point behind
validateAction(), why would you ever want to validate the same stuff two times? In fact you must and should not, because this method is primarily used (and actually forced) in
AJAXProxyAction to ensure no action can be executed without a proper validation.
If you’re using PHP only, you might want to directly create the action object and execute it, skipping the validation. Even though it is possible it violates our general class design and is almost entirely unusable as you’re getting generic error messages in return.
The fancy
AJAXProxyAction is nothing but a common wrapper which reads a specified list of parameters (and unserializes
JSON automatically), executes validation before execution and returns the
JSON-encoded response afterwards. I would say that in 95% of all cases you better stick with this one before creating an own action class for handling.
Let’s take a look at how this magic works, you might want to assume that you’re on AJAX and want to pass the action class “UserOptionAction”, invoking the method “create”. The AJAXProxyAction would invoke validateAction() which itself searches for a method called “
validateCreate()” and executes it, errors are determined by throwing the adequate exception within the method, rather than working with boolean return values. Afterwards the method executeAction() is called which would invoke the method
create() and returns the return values afterwards.
Most likely all your actions will succeed this way, but in some cases the action might fail due to different reasons. One reason might be that you call
validateAction() but now matching
validate*-method exists, in this case the whole requests fails with a permission denied error. If you ever forget to add a
validate*-method, AJAX access will be automatically disabled for this method effectively protected by the framework itself. On the other hand you have to explicitly state those methods which may be called by guests, otherwise requests from guests will be automatically denied.
Three methods worth noticing are
create(),
delete() and
update(), all of them are implemented by
AbstractDatabaseObjectAction but feature an additional layer of security preventing illegal access. These 3 methods are not callable by guests (but you may enable it on your own) and each of them uses an own array called
$permissions* (e.g.
$permissionsCreate) which contents is checked against the user session permissions. The only way to disable these checks (in case your object does not have any permissions) would be overwriting the
validateCreate() method or whichever of those magic three you’re using.
Below is the source code of
PackageUpdateServerAction which shows a typical implementation of
AbstractDatabaseObjectAction, please be aware that you may left out those
$permissions*-arrays if you do not want to allow access on the 3 methods described above:
|
PHP Source code
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
|
<?php
namespace wcf\data\package\update\server;
use wcf\data\AbstractDatabaseObjectAction;
use wcf\system\exception\ValidateActionException;
use wcf\system\WCF;
/**
* (comment stripped)
*/
class PackageUpdateServerAction extends AbstractDatabaseObjectAction {
/**
* @see wcf\data\AbstractDatabaseObjectAction::$className
*/
protected $className = 'wcf\data\package\update\server\PackageUpdateServerEditor';
/**
* @see wcf\data\AbstractDatabaseObjectAction::$permissionsCreate
*/
protected $permissionsCreate = array('admin.system.package.canEditServer');
/**
* @see wcf\data\AbstractDatabaseObjectAction::$permissionsDelete
*/
protected $permissionsDelete = array('admin.system.package.canEditServer');
/**
* @see wcf\data\AbstractDatabaseObjectAction::$permissionsUpdate
*/
protected $permissionsUpdate = array('admin.system.package.canEditServer');
/**
* Validates permissions and parameters
*/
public function validateToggle() {
parent::validateUpdate();
}
/**
* Toggles status.
*/
public function toggle() {
foreach ($this->objects as $server) {
$server->update(array('disabled' => ($server->disabled) ? 0 : 1));
}
}
}
|