Skip to content

Commit d40c1c2

Browse files
author
Andy McCormick
committed
re-adding new add-on structure
1 parent bb91c0d commit d40c1c2

29 files changed

+2288
-1802
lines changed

docs/development/actions.md

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
<!--
2+
This source file is part of the open source project
3+
ExpressionEngine User Guide (https://github.com/ExpressionEngine/ExpressionEngine-User-Guide)
4+
5+
@link https://expressionengine.com/
6+
@copyright Copyright (c) 2003-2020, Packet Tide, LLC (https://packettide.com)
7+
@license https://expressionengine.com/license Licensed under Apache License, Version 2.0
8+
-->
9+
10+
# Actions
11+
12+
[TOC]
13+
14+
## Overview
15+
Actions in ExpressionEngine are URL endpoints that are reached with the `ACT` query parameter. An example of this might be `http://myamazingsite.com/?ACT=43` where 43 is the ID given to an action registered in the `exp_actions` database table. These actions are tied to methods in an add-on which can be used to accept input from forms or run some sort of other functionality defined in the add-on.
16+
17+
NOTE:Before adding an action to your add-on, you need to already have an add-on in place. See [Building An Add-On: Getting Started](development/addon-development-overview.md#getting-started) for how to generate the starter files for your add-on.
18+
19+
## Creating An Amazing Action
20+
To generate an action we use the CLI to add the action to our existing add-on named "Amazing Add-on".
21+
22+
```
23+
php system/ee/eecli.php make:action
24+
```
25+
26+
Follow the prompts to add an action file to your add-on.
27+
28+
29+
This will create an `Actions` folder inside our add-on's folder where will build out the code we want to run when a user hits our `ACT` URL. Inside our `Actions` folder the CLI will create a file with the same name as the method we defined when creating our action.
30+
31+
```
32+
amazing_add_on
33+
┣ Actions
34+
┃ ┗ [MethodName].php
35+
┗...
36+
```
37+
38+
We also notice that a migration is created in our `database/migrations` folder. This migration is ran when your add-on is installed or uninstalled to tell ExpressionEngine what actions we needed added or removed with our add-on.
39+
40+
```
41+
//database/migrations/[timestamp]_[mirgrationname].php
42+
<?php
43+
44+
use ExpressionEngine\Service\Migration\Migration;
45+
46+
class CreateactionamazingaztionforaddonamazingAddOn extends Migration
47+
{
48+
/**
49+
* Execute the migration
50+
* @return void
51+
*/
52+
public function up()
53+
{
54+
ee('Model')->make('Action', [
55+
'class' => 'Amazing_add_on',
56+
'method' => 'AmazingAztion',
57+
'csrf_exempt' => false,
58+
])->save();
59+
}
60+
61+
/**
62+
* Rollback the migration
63+
* @return void
64+
*/
65+
public function down()
66+
{
67+
ee('Model')->get('Action')
68+
->filter('class', 'Amazing_add_on')
69+
->filter('method', 'AmazingAztion')
70+
->delete();
71+
}
72+
}
73+
```
74+
75+
Actions are not available for use until they have been added to the `exp_actions` database table. Actions have the following schema in the database:
76+
77+
| action_id | class | method |csrf_exempt |
78+
|-----------|----------------|----------------|------------|
79+
| 41 | Amazing_add_on | ExampleAction | 0 |
80+
81+
If you want your action to be accessible immediately, you can either run the migration that was created by the CLI or set a flag in the CLI when creating the add-on.
82+
83+
### Run Migration To Activate
84+
After creating your action using the CLI run `$ php system/ee/eecli.php migrate`. When asked for the location, type in the name of your add-on. This will run all migrations that have not been ran for your add-on including entering the action into `exp_actions`.
85+
86+
87+
### Setting Flag To Activate On Creation
88+
On creation of an action, you can also specify to add it to the database after the CLI creates it. You can do this with the `--install` or `-i` flag by running your `make:action` command like so: `$ php system/ee/eecli.php make:action --install`.
89+
90+
91+
92+
## Anatomy of An Action
93+
94+
Once we've added an action to our add-on, an `Actions` folder is created for us. The CLI will generate a class and respective file for us based on the action name we passed to the CLI when creating our action. In this case we added an action named "ExampleAction" to Amazing Add-on.
95+
96+
```
97+
php system/ee/eecli.php make:action
98+
What is the action name? ExampleAction
99+
What add-on is the action being added to? amazing_add_on
100+
Action created successfully!
101+
```
102+
103+
```
104+
amazing_add_on
105+
┣ Actions
106+
┃ ┗ ExampleAction.php
107+
┗...
108+
```
109+
110+
111+
### class [ActionName]
112+
113+
Inside `/Actions/ExampleAction.php` we see the following code generated for us:
114+
115+
```
116+
<?php
117+
118+
namespace ExpressionengineDeveloper\AmazingAddon\Actions;
119+
120+
use ExpressionEngine\Service\Addon\Controllers\Action\AbstractRoute;
121+
122+
class ExampleAction extends AbstractRoute
123+
{
124+
public function process()
125+
{
126+
}
127+
}
128+
```
129+
130+
As we can see, the CLI has correctly created a new class using our action's name in PascalCase as the class name.
131+
132+
Inside of our class is the `process()` method. Anything we want to happen when a user reaches our action should be placed inside this `process()` function.
133+
134+
## The `exp_actions` Database Table
135+
ExpressionEngine's `exp_actions` table is used to provide URL endpoints connected with actions in the core and in add-ons.
136+
137+
The `exp_actions` table is comprised of 4 columns:
138+
139+
| Column Name | Data Type | Description |
140+
|----------------|-------------|--------------------------------------------------------------|
141+
|action_id | int(4) | Action ID given to our action |
142+
|class | varchar(50) | A class name based on our add-on's name |
143+
|method | varchar(50) | Method in our add-on that is ran when this action is executed|
144+
|csrf_exempt | tinyint(1) | Is this endpoint csrf exempt or not |
145+
146+
147+
## Cross Site Request Forgery(CSRF) Exemption
148+
149+
WARN:**Security Alert:**Setting your action to CSRF Exempt, also makes this endpoint less secure though as you are allowing outside connections to your application.
150+
151+
For security reasons, actions are protected by [Cross Site Request Forgery(CSRF)](/development/guidelines/security.md#cross-site-request-forgery). If you want users to be able to reach this endpoint from outside your site (e.g. using cURL to from another domain or application to reach this endpoint and expect data to be returned ) then you will most likely need to make your action CSRF exempt.
152+
153+
- To make your action immediately CSRF exempt, update the `csrf_exempt` value in the `exp_actions` table to be a value of `1`.
154+
- To ensure your action is CSRF exempt for future installations, update the corresponding migration by setting the `csrf_exempt` property to `true`:
155+
```
156+
...
157+
public function up()
158+
{
159+
ee('Model')->make('Action', [
160+
'class' => 'Amazing_add_on',
161+
'method' => 'AmazingAztion',
162+
'csrf_exempt' => true,
163+
])->save();
164+
}
165+
...
166+
```
167+
- To set an action as CSRF exempt on creation, use the `--csrf_exempt` or `-c` flag in the CLI:
168+
169+
```
170+
$ php system/ee/eecli.php make:action --csrf_exempt
171+
```
172+
173+
174+
175+
## Do Something - Build An Action
176+
177+
Let's do something with our action to demonstrate how this would work.
178+
179+
### Form Data
180+
In this example we want to insert a row into our database when a user submits a form.
181+
182+
For this example we'll use a really basic form that would be found in our template which uses our action's endpoint as the action for the form. We know our action's ID from the `exp_actions` table and we're just going to collect the user's first name and last name. We'll then take that information and store it in our database. For the purpose of this example, we'll insert this into a custom table we've added to ExpressionEngine which just has columns `ID`, `first_name`, `last_name`.
183+
184+
185+
Create our action:
186+
```
187+
$ php system/ee/eecli.php make:action --install
188+
What is the action name? ExampleAction
189+
What add-on is the action being added to? [amazing_add_on,...]: amazing_add_on
190+
```
191+
192+
This creates our required files.
193+
194+
Now we had some functionality to our action which will add the first and last name submitted from a form to our custom database table.
195+
196+
Our action's code (`Actions/ExampleAction.php`):
197+
198+
```
199+
<?php
200+
201+
namespace ExpressionengineDeveloper\AmazingAddOn\Actions;
202+
203+
use ExpressionEngine\Service\Addon\Controllers\Action\AbstractRoute;
204+
205+
class ExampleAction extends AbstractRoute
206+
{
207+
public function process()
208+
{
209+
// we'll use the post() method from the core's
210+
// Input Class to grab our POST data and put
211+
// that in our $data array
212+
213+
$data = array(
214+
'first_name' => ee()->input->post('fname'),
215+
'last_name' => ee()->input->post('lname'),
216+
);
217+
218+
ee()->db->insert('our_amazing_table', $data);
219+
220+
return true;
221+
222+
}
223+
}
224+
```
225+
226+
Our template code:
227+
228+
```
229+
<form method="post" action="/?ACT=41">
230+
231+
<label for="fname">First name:</label><br>
232+
<input type="text" id="fname" name="fname" value="John"><br>
233+
<label for="lname">Last name:</label><br>
234+
<input type="text" id="lname" name="lname" value="Doe"><br><br>
235+
<input type="submit" value="Submit">
236+
237+
</form>
238+
239+
```
240+
241+
242+
**A note about action IDs.** For the example above, we looked up the action ID in the database. However, the action ID of your method may be different in your database than someone else as the IDs are auto-incremented by the database on insertion. Therefore, when dynamically creating the form with a custom template tag, it's always best practice to fetch your action ID for use in a template by using the [`fetch_action_id()`](/development/legacy/libraries/cp.md#fetch_action_idclass-method) method from the `CP Class` library.
243+
244+
We would do this in our add-on's template tag with something like this:
245+
246+
```
247+
$aid = ee()->cp->fetch_action_id(`Amazing_add_on`, `ExampleAction`);
248+
```
249+
250+
251+
### Return Data
252+
253+
In this next example we are just creating an endpoint which will be reachable from servers outside of our domain. We are going to expect the application to use cURL or similar library to post an ID to our endpoint. Upon receiving the request our action will return the name of the entry matching the ID if it exists.
254+
255+
Create our action:
256+
```
257+
$ php system/ee/eecli.php make:action --install
258+
What is the action name? ExampleAction
259+
What add-on is the action being added to? [amazing_add_on,...]: amazing_add_on
260+
```
261+
262+
Our action code:
263+
264+
```
265+
<?php
266+
267+
namespace ExpressionengineDeveloper\AmazingAddOn\Actions;
268+
269+
use ExpressionEngine\Service\Addon\Controllers\Action\AbstractRoute;
270+
271+
class ExampleAction extends AbstractRoute
272+
{
273+
public function process()
274+
{
275+
// we'll use the post() method from the core's
276+
// Input Class to grab our POST data and put
277+
// that in our $data array
278+
$entry_id = ee()->input->post('id');
279+
280+
// here we're using the Channel Entry Model
281+
// to request the entry's title
282+
$entry = ee('Model')
283+
->get('ChannelEntry')
284+
->filter('entry_id', $entry_id)
285+
->first();
286+
287+
288+
if ($entry){
289+
$response = $entry->title;
290+
}else{
291+
$response = "No Matching Entry"
292+
}
293+
294+
echo $response;
295+
}
296+
}
297+
```
298+
299+
However, when I attempt to reach this endpoint from another application I get an error returned.
300+
301+
The request:
302+
```
303+
curl --location --request POST 'https://anamzingwebsite.test/?ACT=41' --form 'id="1"'
304+
```
305+
306+
The response:
307+
308+
```
309+
...
310+
<div class="panel redirect">
311+
<div class="panel-heading">
312+
<h3>The following errors were encountered</h3>
313+
</div>
314+
<div class="panel-body">
315+
<ul><li>This form has expired. Please refresh and try again.</li></ul>
316+
</div>
317+
</div>
318+
...
319+
```
320+
This is because of the [`csrf_exempt` value](#cross-site-request-forgerycsrf-exemption) mentioned above. To fix this we can go into the `exp_actions` table of our database and update the `csrf_exempt` column to `1`.
321+
322+
Now when I send that same request, I simply get the entry title I requested:
323+
324+
The response after disabling CSRF protection:
325+
326+
```
327+
...
328+
The Entry You Requested
329+
...
330+
```
331+
Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
---
2+
lang: php
3+
---
4+
5+
<!--
6+
This source file is part of the open source project
7+
ExpressionEngine User Guide (https://github.com/ExpressionEngine/ExpressionEngine-User-Guide)
8+
9+
@link https://expressionengine.com/
10+
@copyright Copyright (c) 2003-2020, Packet Tide, LLC (https://packettide.com)
11+
@license https://expressionengine.com/license Licensed under Apache License, Version 2.0
12+
-->
13+
14+
# The Language File (addon_name_lang.php)
15+
16+
The Language file contains an array named `$lang`, which is used along with the Language class to display text on a page in whatever language is selected in the user's account settings.
17+
18+
## Required Fields
19+
There are two required lines in the language file for each add-on, which allows the name and description of the add-on to be viewable on the MODULES page:
20+
21+
$lang = array(
22+
23+
// Required for MODULES page
24+
25+
'my_module_module_name' => 'Module Name',
26+
'my_module_module_description' => 'Brief description of the module- displayed on the Modules page',
27+
28+
//----------------------------------------
29+
30+
// Additional Key => Value pairs go here
31+
32+
// END
33+
''=>''
34+
);
35+
36+
If the ExpressionEngine core language files contains string with the same key, it will be used in favor of add-on specified string. If an add-on needs to override that string, that can be done by adding it to `$ee_lang` array in the add-on's language file.
37+
38+
## Publish Form Tab Label
39+
40+
In addition to the two required fields you can have a custom tab label for your publish fields. Just assign the desired label to a key which shares the name of your module name:
41+
42+
// Additional Key => Value pairs go here
43+
44+
/**
45+
* Tab Label for publish fields
46+
*
47+
* Assign the label you wish to use to the module_name array key
48+
* Remember only alphanumeric characters, underscores, dashes and spaces are allowed.
49+
*/
50+
51+
'module_name' => 'Tab label'

0 commit comments

Comments
 (0)