Jump to content

Custom (record) actions

From NusaATK

ATK Howto: Custom (record) actions

Complexity: Advanced
Author: Boy Baukema <boy@ibuildings.nl>

List of other Howto's

Defining your own action

Defining your own action for your node is quite easy in ATK, you can do so simply by implementing a function called action_actionname.
Example where we also output our own 'Hello World' page, just like a normal ATK action page:

  function action_showhelloworld()
  {
    $page = &$this->getPage();
  
    $ui = &$this->getUi();
  
    $output = $ui->renderBox(array("title"=>"My Hello World Page",
                                   "content"=>"Hello World!"));
  
    $this->addStyle("style.css");
    $page->addContent($this->renderActionPage("showhelloworld", $output));
  }

Don't forget to also register the node in your module.inc so you can assign rights and let 'normal' users (I'm not saying your users are normal, but you know what I mean :)) use your action as well, instead of only the administrator user.
In our example this would be:

  function getNodes()
  {
    registerNode("worlds.world", array("admin", "add", "edit", "delete","showhelloworld"));
  }

Worlds being the module name and world the node name.

Your own record action

Now say we wanna go a bit further and we want to perform our own action on a record. Say we have a node with data on worlds, and we wanna make a fun action that we could click that would say "Hello $worldname!", so for earth it would be "Hello earth!". We'd have to define it as a recordaction:

  function recordActions($record, &$actions, &$mraactions)
  {
    $actions["showhelloworld"]= session_url(dispatch_url($this->atknodetype(),
                                                         "showhelloworld",
                                                         array("worldname"=>$record["worldname"])));
  }

And change:

  $output = $ui->renderBox(array("title"=>'My Hello World Page',
                                 "content"=>"Hello World!"));
<?php>

to:

<syntaxhighlight lang="php">
  $output = $ui->renderBox(array("title"=>'My Hello World Page',
                                 "content"=>"Hello {$this->m_postvars['worldname']}!"));

Now we can say hello to all the worlds in our node!

Your own record action icon

As a finishing touch we have an icon of a hand (hand.gif) we want to use for this action in the node.
To do this, we need to rename the icon to the actionname, making it showhelloworld.gif.
Now we need somewhere to put this icon where ATK will find it.
In the appropriate module directory (modules/worlds/) create a directory structure like the following:
modules/worlds/themes/default/icons/recordlist/

Now we need to put the icon (showhelloworld.gif) in the directory structure we just created (modules/worlds/themes/default/icons/recordlist/).
And finally, all we need to do is throw away the theme cache (as user with appropriate rights or root user in the application root 'rm -vf atktmp/themes/*') and voila!

Your own multi record action

First, add the NF_MRA flag to your node. Here is a simple example of creating a mra-action:

 function recordActions($record, &$actions, &$mraactions)
 {
   $mraactions['my_mra_action'] = "my_mra_action";
 }

Implement the action_ function:

 function action_my_mra_action()
 {
   atk_var_dump($this->m_postvars["atkselector"], 'selected records'); 
 }

atkselector will contain the primary keys of all records that are selected. You can use this to retrieve the rows and perform the actual action on it.

Don't forget to add the action to the registerNode function in your module.inc


If you want to, for example, update the status of multiple records, your code would look something like this:

 function action_my_mra_validate_action()
 {
   $this->updateStatus('valid', $this->m_postvars["atkselector"]);
    
   return $this->redirect();
 }
  
 function updateStatus($status, $primkeys)
 {
   foreach ($primkeys as $pk)
   {
     $record = array("atkprimkey"=>$pk,
                     "status"=>$status);
     
     $this->updateDb($record);
   }      
 }


Customizing MRA actions

Sometimes simply performing an action is not enough. For instance, you want the user to be able to select multiple records to set an attribute or perform an action with a parmeter on.


Like setting a population status (uninhabitable, unpopulated, populated) for several worlds at once.

You can do this by creating a getCustomMraHtml method in your node. The recordlist will then use the output from this method and place it directly in between the list of actions and the action button. Example:

  public function recordActions($record, $actions, &$mraactions)
  {
    $mraactions['update_population'] = "update_population";
  }

  public function getCustomMraHtml()
  {
     $list = $this->getAttribute('population_status')->edit();
     return $list;
  }

  public function action_update_population()
  {
     $status = $this->getAttribute('population_status')->fetchValue($this->m_postvars);
     foreach ($this->m_postvars['atkselector'] as $pk)
     {
       $record = array("atkprimkey"=>$pk,
                       "population_status"=>$status);
     
       $this->updateDb($record);
     }
  }


NOTE: If you are playing around with NF_MRA you will inevitably notice NF_MRPA (Multi Record Priority Actions) which is a feature that SHOULD allow you to select the order in which records are processed by a multi record action. However this feature should be considered deprecated as no real world usage cases has ever been found beyond the initial one.

A custom form button

Now say you want to have an extra button in your node 'Save and say hello!' besides 'Save and close' and 'Save'.
This is useful in situations where you want to perform a special action on a record besides saving it.
You could add the following method to your node:

  function getFormButtons($mode, $record)
  {
    // Get the normal buttons
    $buttons = parent::getFormButtons($mode,$record);
    if ($mode==='edit')
    {
      // If the user is editting, add a 'saveandsayhello' button (you should also add a translation to your language file for saveandsayhello)
      array_unshift($buttons, '&lt;input type="submit" class="btn_save" name="saveandsayhello" value="'.atktext("saveandsayhello", "worlds").'"&gt;');
    }
    return $buttons;
  }

  function postUpdate($record)
  {
    if ($this->m_postvars['atksaveandsayhello'])
    {
      $this->redirect(dispatch_url($this->atknodetype(),'showhelloworld'));
    }
  }

  function action_update(&$handler)
  {
    // Trick the update handler into thinking that when we clicked
    // the saveandsayhello button, we also clicked the save and close button.
    // You could also use atknoclose if you just want to save
    if ($handler->m_postvars['saveandsayhello']) $handler->m_postvars['atksaveandclose']=1;
    return $handler->action_update();
  }

For more documentation please see:
http://atk.achievo.org/docs/atk/latest/atk/atkNode.html#getFormButtons