Sunday 30 August 2009 3:00:00 pm
Now we will program the heart of our workflow extension, using the eZ Publish API and the variables that were passed to the "execute" function (in eventtypes/events/payperdownload/payperdownloadtype.php in your extension) to perform the role assignment(s). The process of writing this code from scratch would involve browsing through the API and piece by piece code testing (using functions like var_dump(), die(), eZExecution::cleanExit() as needed), which we will not delve into. However, if you are a relative beginner to the eZ Publish API, it is worth browsing through the class lists and definitions to see all that is possible, as you have even more possibilities when you're calling eZ Publish classes in PHP land than with eZ Publish template operators. The classes are named intuitively, and the methods are grouped well and explained in quite a lot of detail. Be sure to take the time to explore the classes, methods, and attributes used in this article.
The last part of our code is where we will start. Before the workflow is accepted and finished running, we do the actual role assignment(s) using the eZRole::assignToUser method:
$role = eZRole::fetch( $roleID ); $role->assignToUser( $userID, $limitIdentifier, $limitValue); eZRole::expireCache();
The first line creates an eZRole object when supplied a role ID. The second line assigns that role to a specified user, with a given limitation type ("Subtree" or "Section"; this will be "Subtree" in our case), and the actual limitation value, which is a path of node IDs to the download file. The last line clears the role cache.
There is quite a bit more code to go, but it is all related to supplying the correct values for the variables used in the parameters above:
There are a few variables that will be consistent for every pay-per-download role assignment. We can define these in an INI file and then call those values in our workflow event. In settings/payperdownload.ini within your extension, enter the following:
<?php /* #?ini charset="utf-8"? [PayPerDownloadSettings] # Which role contains the policy for pay-per-download? RoleID=6 # Which content classes (by identifier) contain pay-per-download files? ContentClasses[] ContentClasses[]=product # Which attribute (by identifier) of the above class contains the pay-per-download file? Attributes[] Attributes[product]=download */ ?>
If you used the same basic Website Interface install that this article used, your "Pay per download" role with have an ID of 6. Be sure to double check what the relevant role ID is on your installation by viewing the role in the Administration Interface. The URL should end in "role/view/<role_id>".
We will access these settings, as well as define the limitation type and user ID, near the top of our "execute" function. Our function should now look like this:
public function execute( $process, $event ) { $ini = eZINI::instance( 'payperdownload.ini' ); // Which role contains the policy for pay-per-download? $roleID = $ini->variable( 'PayPerDownloadSettings', 'RoleID' ); // Which content class (by identifier) contains the pay-per-download file? $contentClasses = $ini->variable( 'PayPerDownloadSettings', 'ContentClasses' ); // Which attribute (by identifier) of the above class contains the pay-per-download file? // Note that this is in an array because later we will pass it to fetchAttributesByIdentifier $attributes = $ini->variable( 'PayPerDownloadSettings', 'Attributes' ); // We want the limit identifier to be by subtree $limitIdentifier = 'Subtree'; // Get the current user $userID = $process->UserID; /* Not going to use these yet // Create the role object $role = eZRole::fetch( $roleID ); // Assign the role $role->assignToUser( $userID, $limitIdentifier, $limitValue); // Clear the role cache eZRole::expireCache(); */ return eZWorkflowType::STATUS_ACCEPTED;
Note the appropriate calls to create an instance of our INI file and to access the variables within the INI file. We were also able to access the user ID from the $process object, which is supplied as part of the standard workflow event framework.
From the list of variables that we need to do the role assignment, we are left with the undefined limitation value.
Recall the the limitation value will be a path of node IDs to the download file, so that the user can download the file associated with each product. Let's take a moment to map out how we are going to get that path:
Information about the current order is also available in the already supplied $process object, as you will see below. The rest of the code is explained within the comments.
[...] // Get the current user $userID = $process->attribute( 'user_id' ); // Get the order ID so that we can find out what objects there were $parameters = $process->attribute( 'parameter_list' ); $orderID = $parameters['order_id']; // Get the order $thisOrder = eZOrder::fetch( $orderID ); // Create the role object $role = eZRole::fetch( $roleID ); // Loop through each product to see whether it's relevant for role assignment foreach ($thisOrder->productItems() as $thisProduct) { $classIdentifier = $thisProduct["item_object"]->ContentObject->attribute( 'class_identifier' ); // Is this in the list of downloadable products? if( in_array( $classIdentifier, $contentClasses ) ) { // We have a match, so the last thing we need to do is to fetch the node of the file // First we want to grab the object so that we can get at its attributes $thisObject = $thisProduct["item_object"]->ContentObject; $dataMap = $thisObject->fetchAttributesByIdentifier( array( $attributes[$classIdentifier] ) ); // There should only be one $dataMap item, so get the path of that foreach( $dataMap as $dataMapAttribute) { $node = eZContentObjectTreeNode::fetchByContentObjectID($dataMapAttribute->attribute( 'data_int' ); // We're only after the main node $limitValue = $node[0]->attribute( 'path_string' ); } // Assign the role $role->assignToUser( $userID, $limitIdentifier, $limitValue); } } // Clear the role cache eZRole::expireCache(); [...]
The additional API code used above involves the following classes:
Notice that the limitation value for each downloadable file is finally defined here:
// We're only after the main node $limitValue = $node[0]->attribute( 'path_string' );
The role assignment is then performed for each downloadable file.
Your complete "execute" function should now look like this:
public function execute( $process, $event ) { $ini = eZINI::instance( 'payperdownload.ini' ); // Which role contains the policy for pay-per-download? $roleID = $ini->variable( 'PayPerDownloadSettings', 'RoleID' ); // Which content class (by identifier) contains the pay-per-download file? $contentClasses = $ini->variable( 'PayPerDownloadSettings', 'ContentClasses' ); // Which attribute (by identifier) of the above class contains the pay-per-download file? // Note that this is in an array because later we will pass it to fetchAttributesByIdentifier $attributes = $ini->variable( 'PayPerDownloadSettings', 'Attributes' ); // We want the limit identifier to be by subtree $limitIdentifier = 'Subtree'; // Get the current user $userID = $process->attribute( 'user_id' ); // Get the order ID so that we can find out what objects there were $parameters = $process->attribute( 'parameter_list' ); $orderID = $parameters['order_id']; // Get the order $thisOrder = eZOrder::fetch($orderID); // Create the role object $role = eZRole::fetch( $roleID ); // Loop through each product to see whether it's relevant for role assignment foreach ($thisOrder->productItems() as $thisProduct) { $classIdentifier = $thisProduct["item_object"]->ContentObject->attribute( 'class_identifier' ); // Is this in the list of downloadable products? if( in_array( $classIdentifier, $contentClasses ) ) { // We have a match, so the last thing we need to do is to fetch the node of the file // First we want to grab the object so that we can get at its attributes $thisObject = $thisProduct["item_object"]->ContentObject; $dataMap = $thisObject->fetchAttributesByIdentifier( array( $attributes[$classIdentifier] ) ); // There should only be one $dataMap item, so get the path of that foreach( $dataMap as $dataMapAttribute) { $node = eZContentObjectTreeNode::fetchByContentObjectID( $dataMapAttribute->attribute( 'data_int' ) ); // We're only after the main node $limitValue = $node[0]->attribute( 'path_string' ); } // Assign the role $role->assignToUser( $userID, $limitIdentifier, $limitValue); } } // Clear the role cache eZRole::expireCache(); return eZWorkflowType::STATUS_ACCEPTED; }
You can now test your workflow event by buying the book product you created. Use the "Test User" account on the front-end, add the product to your shopping cart, initiate the checkout process, then pay for the product using your PayPal Sandbox test buyer account. Then, log in to the Administration Interface using your Administrator user account, navigate to the "Pay-per-download" role, and see that it has been assigned correctly. The "Test User" should be using the "Pay-per-download" role, limited to the download file.
You can also test this by seeing if the Test User account can access "yoursite.com/Media/Downloads/eZ-Publish-Advanced-Content-Management-PDF".