Alternatively you can set up the **$coderoot** property dynamically:
function __construct()
{
$this->coderoot = dirname(__FILE__) . 'helloworld/';
parent::__construct();
}
**Be aware that in phpList, even disabled plugins are instantiated**, even though they are not used. Consequently you should be careful about what you put the constructor of your plugin.
It is certainly OK to initialize properties of the plugin and to do some preprocessing of the settings, but you should do nothing that affects anything outside of your plugin such as the phpList session variables or database tables, including those of the plugin.
Any such work necessary to the operation of the plugin belongs in the //activate()// method. The activate method is invoked **only** for plugins that are enabled, and it is called soon after the plugin is instantiated.
====== Installation ======
To install a plugin, place the plugin.php class file and the code files directory in the PLUGIN_ROOTDIR.
====== Special plugins ======
There are currently three "types" of plugins that are special.
* editor plugin
* authentication plugin
* email sender plugin
Only have one plugin of each type can be enabled.
The [[editor plugin]] implements the editor when editing a campaign or a template.
The [[authentication plugin]] implements the authentication of administrators to the phpList system.
The [[email sender plugin]] performs the actual sending of emails, instead of using SMTP or the php mail() function.
====== phpList plugins and GitHub ======
The plugins are intended to live on GitHub and some of the integration is GitHub specific.
If you want to develop a plugin, first of all give your project a name, and it may be best to add "phpList" in the project name, eg **phplistPluginForSomething**
Then, in your project, add a directory called **plugins** in which you place your plugin files, as described above. This allows you to have multiple plugins per GitHub project.
The "plugins" directory is used to detect the plugin code, when installing with the [[https://www.phplist.org/manual/ch042_phplist-plugins.xhtml|plugin installer]]
The plugin installer will take the URL of the ZIPfile at GitHub and pull the file down, and install it.
But **WATCH OUT!!** In phpList 3.05 (and presumably in earlier 3.x versions) **the plugin installer //may// not install any directory needed by your plugin!** It //may// only install the single plugin class definition file itself. In that case, you can upload the directory and all the files inside it manually yourself.
This is a well-known bug discussed:[[https://mantis.phplist.com/view.php?id=16865|here]] and [[https://mantis.phplist.com/view.php?id=16923|here]]
====== Automated Testing ======
You can set up automated testing on Travis-CI of your plugin. To view how this has been done with the Rest-Api plugin [[plugin automated testing|read the page about it]].
====== Application hooks ======
The file
public $settings = array(
"myplugin_setting1" => array (
'value' => "some default",
'description' => 'Description of this setting',
'type' => "text",
'allowempty' => 0,
"max" => 1000,
"min" => 0,
'category'=> 'general',
),
);
Explanation:
**myplugin\_setting1** -> name of the setting. Use only characters and no spaces. You can then retrieve the current value of this setting in your code with **getConfig('myplugin_setting1')**
**value** -> this is the default for this value, if none are given by the administrator
**description** -> What is it all about
**type** -> this can be text. textarea. boolean, url, integer,
**allowempty** -> 0/1 can this setting be left empty?
**max** and **min** -> used for "integer" only.
**category** -> can be anything, but it will be useful to try to use one of the existing ones. "general", "security", "subscription", "segmentation", "campaign", "reporting"
**Note:** //if want to allow a zero to be entered for an integer setting, you **must** set the //allowedempty// value to 1 for that setting, because Phplist considers a zero value to be an 'empty' setting.//
====== Sql queries ======
Always use the abstraction of the Sql Queries. You can consider the database to be connected and there's no need to connect again.
The abstraction is in //mysql.inc// and the main calls are basically the "mysql\_" functions in PHP, but then without the "my".
* Sql\_Query
* Sql\_Fetch\_Row
* Sql\_Fetch\_Array
* Sql\_Fetch\_Assoc
* Sql\_Escape
* etc
To find out about the database structure, you can read the //structure.php// file.
**NOTE**:// although the database access functions explained above have names derived from the PHP mysql_ functions, phpList is not restricted to using the MySql database, nor the deprecated PHP "mysql\_" functions.
Database access is supported by a ".inc" file defining the functions discussed above. A choice of three different files is available
* //mysql.inc// - mentioned above, supports the deprecated PHP mysql_ functions
* //mysqli.inc// - supports the current PHP mysqli_ functions.
* //adodb.inc// - supports [[http://adodb.sourceforge.net|ADOdb database abstraction library]], which suports many databases including PostGreSQL and Oracle.
The value of the variable $database_module set in the configuration file //config_extended.php// selects which of three ".inc" files is loaded. In phpList v3.012 that variable is found in line 704 of the file.
But whatever database module is loaded the phpList Sql_ functions will work the same way.//
You should note, however, that PHPlist provides a variety of specialized functions for getting data from and storing data in the database, so that you may not need to access the database directly. Here are a few of them, provided as examples:
* listname($listid)
- Provides the name of the list given the numerical list ID.
- See //phplist.php//, line 42
* getListsAsArray()
- Returns the array of list names keyed on the list ID.
- See// phplist.php//, line 138
* getUserAttributeValues($email, $userid=0, $indexByID=false)
- Called by specifying either user email address or ID
- Returns an array of values keyed on the attribute name or 'attribute" concatenated with the attribute ID
- See //userlib.php//, line 218
* existUserID($userid = 0)
- Returns a Boolean false if $userid is not a valid user ID, or the ID back again if it is valid.
- See //userlib.php//, line 202
So it is a good idea, before writing a query directly to the database, to look through the PHPlist codebase, to see whether a function already exists that provides the database access that you require. You will find an index of phplist functions and class methods, starting [[http://resources.phplist.com/develop/functionndx|here]]. These functions and methods are not yet very well documented in this index yet. Often the name of the function will give you an idea of what it does. But the index //does// give you the location of the function or method definition in the codebase, so that you can reference the source code to figure out what the function does and how to use it.
If you must access the database directly note that tables are referenced with the Global tables: $GLOBALS['tables']['tablename']. Note, however, that functions exist in the code base for dropping tables, creating tables, and testing for the existence of tables. In the file //mysql.inc//, //mysqli.inc// or //adodb.inc// found in the //admin/// directory of phpList, look for the functions
*Sql_Drop_Table($table)
*Sql_create_Table ($table,$structure), and
*Sql_Check_For_Table($table)
If you must query the database directly, you need to sanitise your queries to avoid Sql Injection. Use //sprintf// for this. Numbers are sanitised with %d and strings with //Sql_Escape//
Also, it is best practice to avoid doing a **select \*** on tables, just to be sure.
So the result would look something like
$city = 'Amsterdam'; // let's assume this came from external data
$attribute = 10;
$request = Sql_Query(sprintf('select user.id from %s user, %s user_attribute
where user.id = user_attribute.userid
and user_attribute.value = "%s"
and user_attribute.attributeid = %d',
$GLOBALS['tables']['user'],
$GLOBALS['tables']['user_attribute'],
Sql_Escape($city),
$attribute));
The above Query would give you all the subscribers who have attribute 10 to be Amsterdam. Presumably that means attribute 10 is "City".
====== Using PHP program execution functions ======
The short answer here is: **DON'T!**
The PHP functions that we are talking about are those listed [[http://php.net/manual/en/ref.exec.php|here]]. These functions allow you to call the shell to run a command or script, whose output will be returned to the calling program. This is very convenient. It allows you to use a system command as a function in your program. Using one of these functions, say //exec()//, you could incorporate a shell script as a function in your page or a PHP script, or a script written in another language entirely, like Perl or Python.
The problem here is that some system administrators shut off these functions for reasons of security. So if you are writing a plugin for general use, you cannot be sure that your page will function correctly for everyone. Furthermore, such system calls are operating system specific. So what works on Linux won't work on Windows and vice versa.
Consequently you should **avoid making such system calls**, unless you are writing a plugin for a very specific use on an system where you are sure that the necessary PHP functions will be available and that no security issues will arise.
====== Translation ======
To make things translatable use the **s** function. This used to be //$GLOBALS['I18N']->get('text')// and a lot of code still uses that, but the **s** function make this call and makes it much easier to code.
print s('This is some text in the code');
You can also use the //sprintf// format, which will make it easier to translate, as in some languages the structure of the sentence may be different.
print s('%d out of %d messages were sent',$count,$total)
====== Building pages ======
Any page in phpList handles it's own forms and data. Basically the structure of a page is:
if (!defined('PHPLISTINIT')) die(); ## avoid pages being loaded directly
if (isset($_POST['myVariable'])) {
/* check the XSRF token */
if (!verifyToken()) {
print Error(s('Invalid security token, please reload the page and try again'));
return;
}
... sanitise the input variables
... handle the data in the form
return;
}
print (formStart('some additional form elements'));
... show my form
The //formStart()// function specifies the "method" attribute of the form tag as "post". It sets the action attribute to an empty string, so that on submission the page is reloaded, allowing the page itself to process the data that was "posted" to the server.
The //formStart()// accepts a **single string** as an argument. This string should consist of additional attributes to be added to the "form" tag, exactly as they should appear in the tag. For example, to add an id and a class to the "form" tag, you might print the start of the form in the following way:
print (formStart('id="myID" class="myClass"'));
====== Making pages look like phpList ======
Like WordPress, phpList will turn your valid HTML into a nicely formatted web page appropriate to the overall look of the site, the phpList administrative area. An example is show below. {{ wiki:unform.png }} This will work even with a form. Of course, phpList will not insert the necessary HTML tags. You will, for example, need to surround your paragraphs with paragraph tags. And you might want to adjust font-size or text-color in one place or another with an inline style, but phpList will provide for the overall page appearance.
===== Using the UIPanel() class to frame text and forms =====
phpList usually presents its information or a form inside a nice formed box with a gray border and a title at the top as in the image below.{{ wiki:UIP1.png?600 x 392 }}
In this example the HTMLis put into a variable //$dummyText//. Then the box is produced by the following lines of code.
$panel = new UIPanel("An Example of UIPanel()", $dummyText);
print ($panel->display());
That is all that there is to it. You can even put a box inside a box as in the example shown in the next image. {{ wiki:UIP2.png }}
In this example we load the HTML to be in the first box into the variable //$dummyText//. The HTML to go into the second box goes into //$dummyText2//. Then the following code creates the nested boxes.
$panel2 = new UIPanel ("More Dummy Text", $dummyText2);
$dummyText .= $panel2->display(); // Note the combined operator '.='
$panel = new UIPanel("An Example of UIPanel()", $dummyText);
print($panel->display());
Nothing more is required.
To learn more about this class, consult the class definition in //commonlib/lib/interface.php// under the phpList //admin// directory
===== Using the WebblerListing class to create tables =====
Pages commonly to show a tabulation of data contained in an array or data from an //Sql_Query()// accessed with one of the "Sql_Fetch_" functions, such as //Sql_Fetch_Row()//. The WebblerListing class provides several methods that can be used to display such data in nicely formatted tables consistent with the overall look of phpList.
You can imagine the operation of this class as creating an array of table rows, indexed by the value to be entered in the first column of your table. Each row is an array of cells indexed by column names.
After all the data has been entered, the //display()// method produces the HTML which can be printed to put the tabulation onto a page.
You must begin by creating an object to represent your tabulation. You should create this object, passing the passing the name of the leftmost column of your table to the WebblerListing constructor. So you would begin with this code.
$myTable = new WebblerListing("firstCoumnName");
==== The addElement() method ====
The //addElement($value, $url = "", $colsize="")// method adds rows to the tabulation; //$value// is the value (number or text) to be entered into a cell in the first column. If the optional parameter //$url// is given a URL value, a //$value// is linked to that URL in the tabulation. Usually you can omit the //$colsize// argument.
Let's say that you have values and associated URLs in an array //$myArray//. Then you might create a single column tabulation with the //$myTable// object as in the following code.
foreach ($myArray as $anArray) {
$val = $anArray[0];
$url = $anArray[1];
$myTable->addElement($val, $url);
}
if you look at the definition of this method in //admin/connect.php// you will find that the function definition reads
function addElement($name,$url = "",$colsize="")
The first argument is called //$name// instead of //$value//. Originally this method was imagined as creating //row headings// rather entering values into the first cell of a row. But this makes no difference. This argument may contain a numerical value, a string value, or a string serving as a heading for the row.
The one necessary requirement is that **values entered into a tabulation using the //addElement()// method must be unique.** These values are used to index the cells of the additional columns that you add to the table.
==== The addColumn() Method ====
Columns are added to a tabulation with the //addColumn($firstColumnEntry,$column_name,$value,$url="",$align="")// method.
The //$firstColumnEntry// argument determines in which row //$value// appears. The //$column_name// argument determines in which column //$value// appears. New columns are created when this function is first called with a column name that has not appeared before. Of course, the column names must be unique.
As with //addElement()//, //$url// is an optional argument that specifies a link to the value entered into the cell. Usually nothing needs to be entered for //$align//.
The //addColumn()// method calls can be made in any order, since the arguments to each call specify completely where the value entered is to be located.
So now let's say that we need to make a tabulation with three columns. The first column is a numerical id, the second column is a name of some kind, perhaps a building name. Let's suppose the third column is the building height. We also have a url for each id that will be used to hot link the id and the name. Let's say also that we have the data in an array $myArray keyed by the id.
$myTable = new WebblerListing("ID");
foreach($myArray as $key => $anArray) {
$url = $anArray[0];
$name = $anArray[1];
$height = $anArray[2];
$myTable->addElement($key, $url);
$myTable->addColumn($key, "Building", $name, $url);
$myTable->addColumn($key, "Height in Feet", $height);
}
Now you could go ahead an print the table created by this loop:
print($myTable->display())
This would create a nicely formatted table inside the same kind of box that the "UIPanel" class produces. The worm in the apple here is that this table will be given the same title that appears in the first column. There seems to be no facility for creating a table title separate from the heading of the first column in the table. But you can easily change the table title with the PHP function //str_replace()//
$html = $myTable->display();
$oldTitle = 'ID';
$newTitle = 'The Heights of Various Buildings';
$needle = '' . $oldTitle . '
';
$replace = '' . $newTitle . '
';
print(str_replace($needle, $replace, $html));
Notice that we are including the entire "header" div in the search string, as well as the opening div of the "panel" class, because we do not want to accidentally replace anything in the table other than the title.
This code produces the table you see below.{{ wiki:table1.png }}
==== How to page a listing ====
Often you will have a tabulation that is just too long to fit on a single web page. The function //simplePaging($baseurl,$start,$total,$numberPerPage,$itemName = "")// can be used with the //WebblerListing// class to break the tabulation up into separate pages connected by convenient links.
The argument //$baseurl// can be taken to be the name of the page on which the listing occurs, that is, the file name without the '.php' extension; //$start// is the zero-based count at which the current page of the tabulation starts.
Thus the //$start// variable keeps track of which page is being presented. The paging links, next page, previous page, etc., put the appropriate value as a parameter in the URL to be loaded, for example, //&start=30//.
The argument //$total// is the number of items, that is, the number of rows in the tabulation. The argument //$numberPerPage// is, of course, the number of items to be listed on each page; //$itemName// is the name of the kind of items being tabulated, in the examples here, //$itemName// would be "buildings".
Now in the array //$myArray//, let's not key on the building id, but rather create a simple array without a key, in which each subarray contains the building id as the first item. Then we might create a paged listing with the following code in a file, say //myPage.php//
$myTable = new WebblerListing("ID");
$numberPerPage = 10;
$total = count($myArray);
if (isset($_GET("start")) {
$start = $_GET("start"); // The index of the current page is passed as the "start" parameter in the URL
} else {
$start = 0;
}
if (!$total) {
$myTable->addElement('The list of buildings is empty.', ''); //
} else {
for ($i = 0; $i < $numberPerPage; $i++) { // Create a table of proper length for the page.
$anArray = $myArray[$start + $i];
$id = $anArray[0]
$url = $anArray[1];
$name = $anArray[2];
$height = $anArray[3];
$myTable->addElement($id, $url);
$myTable->addColumn($id, "Building", $name, $url);
$myTable->addColumn($id, "Height in Feet", $height);
}
$paging=simplePaging("myPage", $start, $total, $numberPerPage,'buildings');
$myTable->usePanel($paging); // Pass the paging to the $myTable object
}
// Now put the correct title on the table and print it
$html = $myTable->display();
$oldTitle = 'ID';
$newTitle = 'The Heights of Various Buildings';
$needle = '' . $oldTitle . '
';
$replace = '' . $newTitle . '
';
print(str_replace($needle, $replace, $html));
With this code the third page of a building tabulation might appear as shown below.
{{ wiki:table2.png }}
If needed, you can add a class to the opening table tag by calling the //display()// the following way. Let's say that the class you want to add is "myClass." Then you would invoke the display method this way
$html = $myTable->display(0, "myClass");
For more information on the methods of the //WebblerListing// class, consult the class definition in //commonlib/lib/interfacelib.php// under the //admin// directory of phpList.
===== Informing or warning your users =====
To include a notifice on a plugin page you can use the function //Info($message)//. This function returns a string, which can be printed. The result is a yellow box containing your message with a darker yellow border, as shown below.{{ wiki:info.png }}
The message can be HTML, as in the example, or just plain text. Clicking the close button will cause the box to disappear. The box will not be shown again for the rest of the user's session.
If you do not want the user to be able to close the box, print Info() with //true// as a second parameter.
print(Info($message, true));
You can similarly include a warning on a plugin page, using the function //Warn($message)//. The warning appears with red colored text in a red bordered box as seen in the image below. Again the message may be HTML or the plain text seen in the this example. {{ wiki:warn.png }} Notice that the text is always centered in the box and that there is no close button.
The //Warn()// function does not create a string. The function prints the HTML directly on the page.
====== Adding pages to the menus ======
To make your plugin pages show up in the menu system, you need to set a few variables in your plugin.
public $pageTitles = array( // Entries in the plugin menu of the dashboard
'pluginpage' => 'Description of this page in my plugin',
);
public topMenuLinks = array( // Entries in the top menu at the top of each page
'pluginpage' => array('category' => '//[category]//'),
);
The **pluginpage** is a file in your plugin called "pluginpage.php". It lives in the directory you indicated with "coderoot".
The description is used to build the link in the menu.
The //category// needs to be one of the following:
* subscribers
* campaigns
* statistics
* system
* config
* develop (will only show up in "dev" mode)
* info
To ensure that your pages appear in the plugin menu of the dashboard, you should override the //adminMenu()// method of the //defaultplugin.php// with the following code.
function adminMenu() {
return $this->pageTitles;
}
If your plugin has no web pages you should simply return an empty array here. **Note**: If you do not override this method, the //defaultplugin// object will add pages to the menu on its own, including a fictitious "Hello World" page.
====== Linking inside phpList ======
It is not necessary to print raw HTML in order to create links between pages of your plugin or links to other pages in phpList; phpList provides the following functions to do this. For the sake of security, you should use the phpList functions rather than attempting to create a link using hand coded HTML.
The function for definition for each of phpList link-generating functions is found in the file //connect.php//. Each function returns a string. **That string must be printed in order to appear in your page.**
Note that the links produced by these functions usually include a token to avoid [[https://en.wikipedia.org/wiki/Cross-site_request_forgery|cross-site request forgery (CSRF)]]. The token appears at the end of the URL in the form //&tk=hexno// where "hexno" is a hexadecimal number with a random length up to 32 hex digits.
==== PageLink2()====
This function has several arguments, for many of which default values are provided, as seen in the following declaration:
function PageLink2($page,$linkText="",$more_data="",$no_plugin = false,$title = '')
(I have changed the name of some of the arguments to indicate their purpose more clearly.)
If you are using this to link to a page, say //examplePage.php//, of your plugin, say "examplePlugin," you might call this function as follows
PageLink2("examplePage", "This is an important link")
This would generally return a string with random hexadecimal number at the end, looking something like the following:
This is an important link
If you want to pass a piece of data to your page in the query part of the url, you can do that using the third argument of the function.
PageLink2("examplePage", "This is an important link", "mydata")
if you want to pass the value of a quantity 'param' as 'mydata', you would do it as follows:
PageLink2("examplePage", "This is an important link", "param=mydata")
You could similarly pass values for two quantities:
PageLink2("examplePage", "This is an important link", "param1=data1¶m2=data2")
if you want to link to a phpList page outside your plugin,say //setup.php//, you must set //$no_plugin// to //true//, as in the example below.
PageLink2("setup", "This is an important link", "", true)
==== PageLinkClass()====
The arguments of this function are very similar to those of PageLink2():
function PageLinkClass($page,$linkText="",$more_data="", $class="",$title = '')
The function returns a string quite similar what is returned by PageLink2(). Notice though that the argument //$no_plugin// is replaced by //$class//.
This function can be used to produce a link only to pages in your plugin, but not outside your plugin. However, you do have the option of defining a class for the link. So the following call
PageLinkClass("examplePage", "This is an important link, "", "myclass")
produces a string like the following:
This is an important link
==== PageLinkButton() ====
The arguments for this function are exactly the same as the arguments for //PageLinkClass()//.
function PageLinkButton($page,$linkText="",$more_data="", $class="",$title = '')
This function returns a string that will produce a clickable HTML button when printed. The button links to the specified page of your plugin and is labeled with the link text.
A disadvantage to this function is that it cannot be used to produce a button linking to a page //outside// your plugin.You can most easily create such a button using the PHP str_replace() function.
Let's say once more that your plugin is the "examplePlugin". Then if we wanted to make a button linking to the phpList eventLog page with the label "View Event Log", the following code would produce a string creating the desired button when printed:
str_replace("&pi=examplePlugin", "", PageLinkButton("eventLog", "View Event Log"))
====== Using Javascript in your plugin ======
phpList loads both [[http://jquery.com|jQuery]] (version 1.71 in phpList v3.12) and [[https://jqueryui.com|jQueryUI]] (version 1.81 in phpList v3.12). Also loaded are [[http://www.gmarwaha.com/jquery/jcarousellite/|jCarouselLite]], the [[http://isocra.com/2008/02/table-drag-and-drop-jquery-plugin/|JQuery table drag and drop plugin]], as well as the [[http://jquerytools.github.io/documentation/scrollable/|jQuery scrollable tool]]. The jQueryUI javascript includes all of the [[http://api.jqueryui.com/category/widgets/|jQueryUI widgets]], at least all of the widgets available at the time of the release of the version of jQueryUi that is loaded.
Depending on the value of [[system/config/use_minified_assets]] the jQuery is loaded in the header or the footer of the page. By default [[system/config/use_minified_assets]] is true, which means it is loaded in the footer.
To ensure that your Javascript works, particularly when it depends on jQuery, you need to add it to a global variable, pagefooter.
$pagefooter['unique_value'] = '
Make sure to use a proper unique value for //unique\_value//, for example, based on the name of your plugin, to avoid it clashing with other plugins. A way to create a good unique value for the index is to simply append the name of your page (without ".php") to the name of your plugin. For example if your plugin is //MyCoolPlugin// and you are writing code for //myCoolPage.php//, you might take "MyCoolPlugin_myCoolPage" as your index for the //$pagefooter// array.
====== Javascript modal dialogs ======
As described in the previous topic, phpList automatically loads jQuery and jQueryUI.
You are free to use any of the tools provided by these Javascript libraries. However, you should note that jQueryUI requires a stylesheet to provide its functionality. This stylesheet is loaded with Phplist. **However**, Phplist loads a stylesheet, //style.css//, **after** the jQueryUI stylesheet. Thus, this Phplist stylesheet can, and in fact **does**, override some of the styles that jQuery requires in its widgets. This can result in a widget failing to produce the behavior described for it in the jQueryUI documentation.
This is certainly the case with [[http://api.jqueryui.com/dialog/|jQueryUI modal dialogs]]. Vertical positioning does not work. This is due to a style loaded with //style.css//. A modal dialog is centered horizontally by default, as it is supposed to be, but the dialog appears at the **top** of the page, despite all reasonable efforts to restore the default vertical centering.
It is good practice to put scripts at the bottom of a webpage, after your HTML. The default vertical centering of modal dialogs can then be restored by adding the following style after ////.
If you should have trouble with a jQueryUI widget, You should check out the styles that the widget needs to function, and then check //style.css// to see if those have been overridden. (**Mariela Zarate** must be credited with developing the fix for the vertical positioning of modal dialogs.)
====== Building an Ajax server page ======
Since phpList does load [[http://jquery.com|jQuery]], you can use [[https://en.wikipedia.org/wiki/Ajax_(programming)|Ajax]] in a Javascript section added to a plugin web page or form. You can make the Javascript call using the [[http://api.jquery.com/category/ajax/|jQuery functions]] [[http://api.jquery.com/jQuery.ajax/|$.ajax()]] or more simply [[http://api.jquery.com/jQuery.post/|$.post()]]. The URL for such an Ajax call would be the following
index.php?page=exampleAjax&pi=examplePlugin&ajaxed=1
or more concisely
?page=exampleAjax&pi=examplePlugin&ajaxed=1
or alternatively
./?page=exampleAjax&pi=examplePlugin&ajaxed=1
where the plugin name is //examplePlugin// and the Ajax server page is //exampleAjax.php//.
Including "//ajaxed=1//" in the URL is not absolutely necessary, but it will suppress some unwanted output from phpList and possibly speed up the processing of the Ajax call.
An Ajax call make take some time to complete. A convenient spinner to display on the calling page while waiting is found at the relative URL //images/busy.gif//.
A page to be called through Ajax is not really a command line page, although it does share some features with a command line page. First, you should begin your page with the following code:
if (!defined('PHPLISTINIT')) die(); ## avoid pages being loaded directly
Second, just as with the command line page, you must have the following statement before you produce any output, in order to prevent unwanted output from //index.php// being sent to the caller.
ob_end_clean();
The output of your page must be a string, which you can return to the caller with an //echo// or //print()// statement.
After completing its output, the page should either //exit;// or //die();//. You should **NOT** conclude the output with a //return;// since you will be returning to //index.php// which may produce output unexpected by the Ajax caller.
Although an Ajax server page is like a command line page, phpList does not consider it to be a command line page and the page does not need to be entered into the //$commandlinePluginPages// array.
**There is another way to do Ajax. You might do it the way phpList does.**
===== How phpList does Ajax =====
Instead of using the jQuery functions //$.ajax()// or //$.post()//, phpList uses the [[https://api.jquery.com/load/|load()]] method of a jQuery object to load the body of phpList page into the parent DOM element containing an "**ajaxable**" link. The page is a very simple page containing just the result of whatever operation is specified by the link.
This operation is set up by phpList when //index.php// loads.Among other things the //$(document).ready()// method attaches a click handler to each link belonging to the CSS class "ajaxable". This handler changes the 'href' of the link, so that it points to the //pageaction// page. The //pageaction// page prints a complete, but very simple web page. Then the //.load()// method of the parent element of the link is used to replace all the HTML contained in that element with the page body, including the link.
The file //pageaction.php// does not do much by itself. It produces simple versions of the required HTML headers and a body tag. The work is done by the file included from the //admin/actions/// directory. The included page is specified by the 'action' argument of the URL passed to the //load()// method. After the page is included, //pageaction.php// prints a closing body tag, so that a complete proper web page is produced.
if the action argument of the URL is '&action=actionpage', then //actionpage.php// will be included from the //admin/actions/// directory. If no action argument is included in the original 'href' of the link, the name of the action page is taken to be the same as that of the page in which the link is found.
This method of doing Ajax has the advantage that no additional Javascript is required for a link to create an Ajax call. All that is required is to create an appropriate action page, to place it in the //actions// subdirectory, and to add an 'action' argument to the URL referenced by the link.
The disadvantage is that doing Ajax this way very much limits what can be done. All that you can do is to fill a DOM element with the HTML loaded from the action page. There is no way to do any post-processing of the data returned from the call.
===== Doing Ajax the phpList way =====
A plugin can have "ajaxable" links too. The Javascript click handler will send a call to your plugin. To process this call, the plugin **must** have a file //pageaction.php// in the [[https://resources.phplist.com/develop/plugins#example|coderoot directory]] of the plugin.
The action pages for the plugin can be located in any convenient location inside the //coderoot// directory of the plugin. The simplest procedure might be to locate these pages inside an 'actions' subdirectory inside //coderoot//.
Your file //pageaction.php// should contain the following code.
@ob_end_clean();
@ob_start();
verifyCsrfGetToken(); // Prevent cross site request forgery
if (is_file(dirname(__FILE__).'/ui/'.$GLOBALS['ui'].'/pagetop_minimal.php')) {
include_once dirname(__FILE__).'/ui/'.$GLOBALS['ui'].'/pagetop_minimal.php';
}
$status = s('Failed');
... Include the proper page here, determined by the action argument.
... This page should produce a string value for the $status variable.
print $status;
print '