meta data for this page
Differences
This shows you the differences between two versions of the page.
Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
develop:plugins [2021/06/05 10:35] – [phpList plugins and Github] marianaphplist | develop:plugins [2023/10/27 20:17] (current) – duncanc | ||
---|---|---|---|
Line 1: | Line 1: | ||
+ | |||
+ | ====== How to Write a phpList Plugin ====== | ||
+ | |||
+ | A plugin can add extra functionality to phplist in either, or both, of two ways: | ||
+ | * By providing extra pages. The page can do almost anything, such as providing a report, or some extra processing. | ||
+ | * By hooking into core phplist at certain points in its processing, such as when sending a campaign. | ||
+ | |||
+ | A plugin is implemented as a class that extends the core phplistPlugin class. Plugin classes reside in the directory set by the constant // | ||
+ | |||
+ | The default location for plugins is the " | ||
+ | |||
+ | If your plugin provides extra pages, then the php files for those pages should be in a subdirectory named after the plugin. | ||
+ | |||
+ | All phpList pages are loaded through // | ||
+ | |||
+ | So, imagine we are running with a secure connection for the site " | ||
+ | |||
+ | You can then access a plugin page similarly, setting the " | ||
+ | ===== Example ===== | ||
+ | |||
+ | If your plugin is called // | ||
+ | |||
+ | Further files associated with the plugin must go into the "// | ||
+ | |||
+ | The plugin constructor of the plugin class must call the parent constructor to ensure that the plugin is initialised correctly. | ||
+ | |||
+ | < | ||
+ | <?php | ||
+ | class helloworld extends phplistPlugin | ||
+ | { | ||
+ | public $coderoot = PLUGIN_ROOTDIR . '/ | ||
+ | |||
+ | function __construct() | ||
+ | { | ||
+ | parent:: | ||
+ | } | ||
+ | ... | ||
+ | } | ||
+ | ?></ | ||
+ | |||
+ | Alternatively you can set up the **$coderoot** property dynamically: | ||
+ | |||
+ | < | ||
+ | function __construct() | ||
+ | { | ||
+ | $this-> | ||
+ | parent:: | ||
+ | }</ | ||
+ | |||
+ | **Be aware that in phpList, even disabled plugins are instantiated**, | ||
+ | |||
+ | 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 // | ||
+ | ====== 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 " | ||
+ | |||
+ | * 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 " | ||
+ | |||
+ | 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 " | ||
+ | |||
+ | 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: | ||
+ | |||
+ | ====== 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 < | ||
+ | |||
+ | If you encounter some of these hooks to not work, open a [[https:// | ||
+ | |||
+ | You may be disappointed to find that none of the methods provided in // | ||
+ | |||
+ | Think of the methods of // | ||
+ | |||
+ | An example of this technique is a plugin prefixing the subject line of messages with the name of the list to which the message is being sent. (See http:// | ||
+ | |||
+ | Now looking at the " | ||
+ | |||
+ | You can use one of these methods to get the data needed. It is not necessary to alter the return value from the default value. | ||
+ | |||
+ | Looking further through the phpList code, you will find that the last method of // | ||
+ | |||
+ | So if you want to prefix the subject line of the message with the list name, // | ||
+ | |||
+ | You will find another example of this kind of coding if you examine the Subject Line Placeholder plugin at http:// | ||
+ | |||
+ | Unfortunately few of the class methods of // | ||
+ | |||
+ | All you have to do is to prefix the argument with an ampersand in your method definition: | ||
+ | function campaignStarted(& | ||
+ | mycode ... | ||
+ | } | ||
+ | |||
+ | This change will will allow you to modify the // | ||
+ | yourplugin-> | ||
+ | But the PHP engine knows from your method definition that it is not the // | ||
+ | |||
+ | **You should be very careful with this last coding technique.** You might really screw up the operation of the software if you don't know what you are doing! | ||
+ | ====== Adding settings ====== | ||
+ | |||
+ | If you have things you want the administrator to configure in order for your plugin to work, you can add settings, which will show up on the " | ||
+ | |||
+ | To do this define a public variable in your plugin called **settings** | ||
+ | |||
+ | < | ||
+ | |||
+ | public $settings = array( | ||
+ | " | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | ' | ||
+ | " | ||
+ | " | ||
+ | ' | ||
+ | ), | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | 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(' | ||
+ | |||
+ | **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 " | ||
+ | |||
+ | **category** -> can be anything, but it will be useful to try to use one of the existing ones. " | ||
+ | |||
+ | **Note:** //if want to allow a zero to be entered for an integer setting, you **must** set the // | ||
+ | |||
+ | ====== Sql queries ====== | ||
+ | |||
+ | Always use the abstraction of the Sql Queries. You can consider the database to be connected and there' | ||
+ | |||
+ | The abstraction is in // | ||
+ | |||
+ | * 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 // | ||
+ | |||
+ | **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 " | ||
+ | |||
+ | Database access is supported by a " | ||
+ | * // | ||
+ | * // | ||
+ | * // | ||
+ | |||
+ | The value of the variable $database_module set in the configuration 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 // | ||
+ | * getListsAsArray() | ||
+ | - Returns the array of list names keyed on the list ID. | ||
+ | - See// phplist.php//, | ||
+ | * getUserAttributeValues($email, | ||
+ | - Called by specifying either user email address or ID | ||
+ | - Returns an array of values keyed on the attribute name or ' | ||
+ | - See // | ||
+ | * 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 // | ||
+ | |||
+ | 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:// | ||
+ | |||
+ | If you must access the database directly note that tables are referenced with the Global tables: $GLOBALS[' | ||
+ | |||
+ | *Sql_Drop_Table($table) | ||
+ | *Sql_create_Table ($table, | ||
+ | *Sql_Check_For_Table($table) | ||
+ | |||
+ | If you must query the database directly, you need to sanitise your queries to avoid Sql Injection. | ||
+ | |||
+ | Also, it is best practice to avoid doing a **select \*** on tables, just to be sure. | ||
+ | |||
+ | So the result would look something like | ||
+ | |||
+ | < | ||
+ | |||
+ | $city = ' | ||
+ | $attribute = 10; | ||
+ | |||
+ | $request = Sql_Query(sprintf(' | ||
+ | where user.id = user_attribute.userid | ||
+ | and user_attribute.value = " | ||
+ | and user_attribute.attributeid = %d', | ||
+ | $GLOBALS[' | ||
+ | $GLOBALS[' | ||
+ | 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 " | ||
+ | ====== Using PHP program execution functions ====== | ||
+ | |||
+ | The short answer here is: **DON' | ||
+ | |||
+ | The PHP functions that we are talking about are those listed [[http:// | ||
+ | |||
+ | 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, | ||
+ | |||
+ | 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 // | ||
+ | |||
+ | < | ||
+ | print s(' | ||
+ | </ | ||
+ | |||
+ | 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', | ||
+ | </ | ||
+ | |||
+ | |||
+ | |||
+ | ====== Building pages ====== | ||
+ | |||
+ | Any page in phpList handles it's own forms and data. Basically the structure of a page is: | ||
+ | |||
+ | < | ||
+ | |||
+ | if (!defined(' | ||
+ | |||
+ | if (isset($_POST[' | ||
+ | |||
+ | /* check the XSRF token */ | ||
+ | if (!verifyToken()) { | ||
+ | print Error(s(' | ||
+ | | ||
+ | } | ||
+ | |||
+ | ... sanitise the input variables | ||
+ | |||
+ | ... handle the data in the form | ||
+ | |||
+ | | ||
+ | } | ||
+ | |||
+ | print (formStart(' | ||
+ | |||
+ | |||
+ | ... show my form | ||
+ | |||
+ | </ | ||
+ | |||
+ | </ | ||
+ | The // | ||
+ | |||
+ | The // | ||
+ | < | ||
+ | ====== 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: | ||
+ | |||
+ | ===== 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: | ||
+ | |||
+ | In this example the HTMLis put into a variable // | ||
+ | < | ||
+ | print ($panel-> | ||
+ | |||
+ | 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: | ||
+ | In this example we load the HTML to be in the first box into the variable // | ||
+ | < | ||
+ | $dummyText .= $panel2-> | ||
+ | $panel = new UIPanel(" | ||
+ | print($panel-> | ||
+ | |||
+ | Nothing more is required. | ||
+ | |||
+ | To learn more about this class, consult the class definition in // | ||
+ | ===== Using the WebblerListing class to create tables ===== | ||
+ | |||
+ | Pages commonly to show a tabulation of data contained in an array or data from an // | ||
+ | |||
+ | 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 // | ||
+ | |||
+ | 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. | ||
+ | |||
+ | < | ||
+ | |||
+ | ==== The addElement() method ==== | ||
+ | |||
+ | The // | ||
+ | |||
+ | Let's say that you have values and associated URLs in an array // | ||
+ | < | ||
+ | $val = $anArray[0]; | ||
+ | $url = $anArray[1]; | ||
+ | $myTable-> | ||
+ | }</ | ||
+ | |||
+ | if you look at the definition of this method in // | ||
+ | < | ||
+ | |||
+ | The one necessary requirement is that **values entered into a tabulation using the // | ||
+ | ==== The addColumn() Method ==== | ||
+ | |||
+ | Columns are added to a tabulation with the // | ||
+ | |||
+ | The // | ||
+ | |||
+ | As with // | ||
+ | |||
+ | The // | ||
+ | |||
+ | 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. | ||
+ | |||
+ | < | ||
+ | foreach($myArray as $key => $anArray) { | ||
+ | $url = $anArray[0]; | ||
+ | $name = $anArray[1]; | ||
+ | $height = $anArray[2]; | ||
+ | $myTable-> | ||
+ | $myTable-> | ||
+ | $myTable-> | ||
+ | }</ | ||
+ | |||
+ | Now you could go ahead an print the table created by this loop: | ||
+ | < | ||
+ | This would create a nicely formatted table inside the same kind of box that the " | ||
+ | |||
+ | < | ||
+ | $html = $myTable-> | ||
+ | $oldTitle = ' | ||
+ | $newTitle = 'The Heights of Various Buildings'; | ||
+ | $needle = '< | ||
+ | $replace = '< | ||
+ | print(str_replace($needle, | ||
+ | Notice that we are including the entire " | ||
+ | |||
+ | This code produces the table you see below.{{ wiki: | ||
+ | ==== 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 // | ||
+ | |||
+ | The argument // | ||
+ | |||
+ | 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, //& | ||
+ | |||
+ | The argument //$total// is the number of items, that is, the number of rows in the tabulation. The argument // | ||
+ | |||
+ | Now in the array // | ||
+ | < | ||
+ | $myTable = new WebblerListing(" | ||
+ | $numberPerPage = 10; | ||
+ | $total = count($myArray); | ||
+ | if (isset($_GET(" | ||
+ | $start = $_GET(" | ||
+ | } else { | ||
+ | $start = 0; | ||
+ | } | ||
+ | if (!$total) { | ||
+ | $myTable-> | ||
+ | } else { | ||
+ | for ($i = 0; $i < $numberPerPage; | ||
+ | $anArray = $myArray[$start + $i]; | ||
+ | $id = $anArray[0] | ||
+ | $url = $anArray[1]; | ||
+ | $name = $anArray[2]; | ||
+ | $height = $anArray[3]; | ||
+ | $myTable-> | ||
+ | $myTable-> | ||
+ | $myTable-> | ||
+ | } | ||
+ | $paging=simplePaging(" | ||
+ | $myTable-> | ||
+ | } | ||
+ | |||
+ | // Now put the correct title on the table and print it | ||
+ | $html = $myTable-> | ||
+ | $oldTitle = ' | ||
+ | $newTitle = 'The Heights of Various Buildings'; | ||
+ | $needle = '< | ||
+ | $replace = '< | ||
+ | print(str_replace($needle, | ||
+ | </ | ||
+ | |||
+ | With this code the third page of a building tabulation might appear as shown below. | ||
+ | {{ wiki: | ||
+ | If needed, you can add a class to the opening table tag by calling the // | ||
+ | < | ||
+ | |||
+ | For more information on the methods of the // | ||
+ | ===== Informing or warning your users ===== | ||
+ | |||
+ | To include a notifice on a plugin page you can use the function // | ||
+ | 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. | ||
+ | |||
+ | < | ||
+ | |||
+ | You can similarly include a warning on a plugin page, using the function // | ||
+ | |||
+ | 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 | ||
+ | ' | ||
+ | ); | ||
+ | | ||
+ | public topMenuLinks = array( // Entries in the top menu at the top of each page | ||
+ | ' | ||
+ | ); | ||
+ | </ | ||
+ | |||
+ | The **pluginpage** is a file in your plugin called " | ||
+ | |||
+ | The description is used to build the link in the menu. | ||
+ | |||
+ | The // | ||
+ | | ||
+ | | ||
+ | * subscribers | ||
+ | * campaigns | ||
+ | * statistics | ||
+ | * system | ||
+ | * config | ||
+ | * develop (will only show up in " | ||
+ | * info | ||
+ | |||
+ | To ensure that your pages appear in the plugin menu of the dashboard, you should override the // | ||
+ | |||
+ | < | ||
+ | return $this-> | ||
+ | } | ||
+ | |||
+ | </ | ||
+ | |||
+ | If your plugin has no web pages you should simply return an empty array here. **Note**: If you do not override this method, the // | ||
+ | |||
+ | ====== 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 // | ||
+ | |||
+ | Note that the links produced by these functions usually include a token to avoid [[https:// | ||
+ | |||
+ | ==== PageLink2()==== | ||
+ | |||
+ | This function has several arguments, for many of which default values are provided, as seen in the following declaration: | ||
+ | < | ||
+ | If you are using this to link to a page, say // | ||
+ | < | ||
+ | This would generally return a string with random hexadecimal number at the end, looking something like the following: | ||
+ | < | ||
+ | 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. | ||
+ | < | ||
+ | if you want to pass the value of a quantity ' | ||
+ | < | ||
+ | You could similarly pass values for two quantities: | ||
+ | < | ||
+ | if you want to link to a phpList page outside your plugin,say // | ||
+ | < | ||
+ | ==== PageLinkClass()==== | ||
+ | |||
+ | The arguments of this function are very similar to those of PageLink2(): | ||
+ | < | ||
+ | The function returns a string quite similar what is returned by PageLink2(). Notice though that the argument // | ||
+ | |||
+ | 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 | ||
+ | < | ||
+ | produces a string like the following: | ||
+ | < | ||
+ | ==== PageLinkButton() ==== | ||
+ | The arguments for this function are exactly the same as the arguments for // | ||
+ | < | ||
+ | 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 " | ||
+ | < | ||
+ | |||
+ | ====== Using Javascript in your plugin ====== | ||
+ | |||
+ | phpList loads both [[http:// | ||
+ | |||
+ | Depending on the value of [[system/ | ||
+ | |||
+ | To ensure that your Javascript works, particularly when it depends on jQuery, you need to add it to a global variable, pagefooter. | ||
+ | |||
+ | < | ||
+ | | ||
+ | |||
+ | // put your JS here | ||
+ | |||
+ | </ | ||
+ | </ | ||
+ | |||
+ | Make sure to use a proper unique value for // | ||
+ | |||
+ | ====== 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**, | ||
+ | |||
+ | This is certainly the case with [[http:// | ||
+ | |||
+ | 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 //</ | ||
+ | < | ||
+ | < | ||
+ | .ui-dialog{top: | ||
+ | </ | ||
+ | </ | ||
+ | |||
+ | If you should have trouble with a jQueryUI widget, You should check out the styles that the widget needs to function, and then check // | ||
+ | |||
+ | ====== Building an Ajax server page ====== | ||
+ | |||
+ | Since phpList does load [[http:// | ||
+ | < | ||
+ | or more concisely | ||
+ | < | ||
+ | or alternatively | ||
+ | < | ||
+ | where the plugin name is // | ||
+ | |||
+ | Including "// | ||
+ | |||
+ | 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 // | ||
+ | |||
+ | 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: | ||
+ | < | ||
+ | < | ||
+ | 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 // | ||
+ | |||
+ | 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 // | ||
+ | |||
+ | **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 // | ||
+ | |||
+ | This operation is set up by phpList when // | ||
+ | |||
+ | The file // | ||
+ | |||
+ | if the action argument of the URL is '& | ||
+ | |||
+ | 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, | ||
+ | |||
+ | 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 " | ||
+ | |||
+ | The action pages for the plugin can be located in any convenient location inside the // | ||
+ | |||
+ | Your file // | ||
+ | |||
+ | < | ||
+ | @ob_start(); | ||
+ | |||
+ | verifyCsrfGetToken(); | ||
+ | |||
+ | if (is_file(dirname(__FILE__).'/ | ||
+ | include_once dirname(__FILE__).'/ | ||
+ | } | ||
+ | |||
+ | $status = s(' | ||
+ | |||
+ | ... Include the proper page here, determined by the action argument. | ||
+ | ... This page should produce a string value for the $status variable. | ||
+ | |||
+ | print $status; | ||
+ | print '</ | ||
+ | exit; </ | ||
+ | |||
+ | You can produce ' | ||
+ | |||
+ | < | ||
+ | This would produce the following string to be printed on your page: | ||
+ | < | ||
+ | If the ' | ||
+ | < | ||
+ | In this case the click handler will be set the ' | ||
+ | |||
+ | An ' | ||
+ | |||
+ | **NOTE**: //To insure that Ajax works properly, you should always use the name of the page on which the " | ||
+ | |||
+ | Also note, that these links can be made to work, but without the Ajax interactivity, | ||
+ | ======Writing plugin pages for the command line====== | ||
+ | Many Phplist pages can be run from the command line. Similarly it is also possible to set up some pages of a plugin to be run from the command line. | ||
+ | |||
+ | Let's assume that you have written a plugin named **Xplugin**. Remember that **Xplugin** will have a class definition extending the **phplistPlugin** class. It will be a subclass of that parent class. | ||
+ | |||
+ | Suppose you want to create pages // | ||
+ | |||
+ | Further you must have a property $commandlinePages in the definition of the **Xplugin** subclass. This property will be an array of pages that can be run from the command line. But the pages are entered into the array without their //.php// extension, as follows: | ||
+ | < | ||
+ | public $commandlinePages = array (' | ||
+ | </ | ||
+ | It is not necessary to specify the extension here, because only plugin pages with a //.php// extension can be run from the command line. | ||
+ | |||
+ | **Note that consulting // | ||
+ | < | ||
+ | # These files can be called from the commandline | ||
+ | # This should hold an array per file: filename (without .php) => path relative to admin/ | ||
+ | public $commandlinePluginPages = array(); | ||
+ | </ | ||
+ | The second comment here is **dead wrong**. The path is not relative to //admin///. It is relative to the plugin root directory specified in the **$coderoot** property. Furthermore, | ||
+ | |||
+ | ====Running Your Pages From the Command Line=== | ||
+ | |||
+ | It is possible to run certain Phplist and plugin pages using the [[http:// | ||
+ | * **-p**: the file name (without //.php//) to be run | ||
+ | * **-m**: the name of the plugin to which the page belongs. This argument is not needed for Phplist pages, such as // | ||
+ | * **-c**: the path to the Phplist config file. | ||
+ | You should have no space between the letter representing the argument, for example **-p**, and the value of the argument. | ||
+ | |||
+ | Suppose that the path to the **phplist** directory is /// | ||
+ | < | ||
+ | php / | ||
+ | </ | ||
+ | To run // | ||
+ | < | ||
+ | php / | ||
+ | </ | ||
+ | You cannot run any page of your plugin without passing in the name of the plugin as the **-m** argument to // | ||
+ | |||
+ | If you want to run your page as a cron job, you must specify the complete path to the relevant php interpreter for your installation. So **php** in the invocation above must be replaced by:< | ||
+ | ====Composing a Page=== | ||
+ | As shown in the **Building Pages** section above, you should not allow your page to be run except through Phplist itself. So at the top of your code you should have: | ||
+ | < | ||
+ | if (!defined(' | ||
+ | </ | ||
+ | |||
+ | If you are planning to print to the command line, you should next have the following: | ||
+ | < | ||
+ | ob_end_clean(); | ||
+ | </ | ||
+ | |||
+ | The Phplist index file writes a lot of HTML to an output buffer, even though you are calling the file from the command line. The function above will throw away the buffered material, so that the only output is what your page writes (and of course whatever error messages result from your code.) | ||
+ | |||
+ | If you want your page to serve as a web page as well, you can use the **$GLOBALS[' | ||
+ | < | ||
+ | <? | ||
+ | if (!defined(' | ||
+ | if ($GLOBALS[" | ||
+ | ob_end_clean(); | ||
+ | |||
+ | Your command line stuff | ||
+ | | ||
+ | ... | ||
+ | | ||
+ | } else { | ||
+ | |||
+ | Your web stuff | ||
+ | | ||
+ | ... | ||
+ | | ||
+ | } | ||
+ | |||
+ | ... | ||
+ | |||
+ | Function definitions used in either mode | ||
+ | | ||
+ | ... | ||
+ | |||
+ | ?> | ||
+ | </ | ||
+ | |||
+ | ==== Command Line Arguments ==== | ||
+ | |||
+ | Besides the **-p** argument identifying your plugin page and the **-m** argument identifying the name of your plugin, you might want to pass other other arguments to the page. For example, you might want to pass two arguments, say //arg1// and //arg2//. You might pass these, say, as the **-x** and **-y** options. What letter you use for each is up to you. You just have to use a **single letter**, other than **p** or **m**. | ||
+ | |||
+ | Phplist is very kind to you; you don't have to worry about picking the values of these options off the command line yourself. You will find those values saved for you in the //$cline// array, keyed by the single that you have chosen to identify each option. Thus to use the value of //argi//, you would refer to // | ||
+ | |||
+ | So if, for example, you need to get the particular plugin page a method is running on, you could find that as // | ||
+ | |||
+ | < | ||
+ | |||
+ | Alternatively you can use the $GLOBALS array, which does not require such a declaration. So you could, for example, get the value of //arg2// as // | ||
+ | |||
+ | The name of the page running is also available in the superglobal //$_GET//. Thus the name of the page can be retrieved as // | ||
+ | ====== Public pages ====== | ||
+ | |||
+ | A plugin can provide a public page, similar to the phplist subscribe or preferences pages. You can create a URL pointing to your page using the following code: | ||
+ | |||
+ | < | ||
+ | $theURL .= "? | ||
+ | |||
+ | This code would produce a URL similar to the following: | ||
+ | |||
+ | < | ||
+ | |||
+ | **NOTE:** //if you are generating a link to a public page from a command line script, the item // | ||
+ | |||
+ | If you need the ' | ||
+ | < | ||
+ | // | ||
+ | ===== Configuring your plugin to use public pages ===== | ||
+ | To provide a public page, a plugin has to override the // | ||
+ | |||
+ | < | ||
+ | |||
+ | As for admin pages, each page must have a corresponding file in the // | ||
+ | |||
+ | A page can then generate its content in any way it wants. It has access to any URL query parameters through $_GET and to the phplist database, as well as constants defined in the phplist configuration files and the functions defined in // | ||
+ | < | ||
+ | where of course the number of arguments depends on your definition of the method. | ||
+ | ==== Page header and footer ==== | ||
+ | |||
+ | The output does not automatically use the " | ||
+ | If you want the page to have the same theme as other phplist pages then the plugin must add those to the output. | ||
+ | See the function confirmPage() in the file index.php for an example of how to use the header and footer, and also to set the page title | ||
+ | |||
+ | < | ||
+ | $res .= $GLOBALS[' | ||
+ | $res .= '< | ||
+ | $res .= $html; | ||
+ | $res .= "< | ||
+ | $res .= $GLOBALS[' | ||
+ | |||
+ | ==== Authentication ==== | ||
+ | |||
+ | By default a page is totally public, visible to anyone, similar to the subscribe page. If you want a page to be available only to subscribers, | ||
+ | |||
+ | < | ||
+ | |||
+ | The plugin should then validate that the UID is for an existing phplist subscriber. | ||
+ | |||
+ | |||
+ | ====== Packaging and publishing ====== | ||
+ | |||
+ | When you are ready to publish your plugin, please make sure that you have a composer.json file in the root of your plugin repository, which contains at least the " | ||
+ | |||
+ | The type should always be " | ||
+ | |||
+ | An example is | ||
+ | |||
+ | { | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | ], | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | " | ||
+ | }, | ||
+ | " | ||
+ | " | ||
+ | } | ||
+ | } | ||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | |||
+ | From version 2.11.8 onwards, phpList has an auto-installer for plugins. This currently works with GitHub only. The '' | ||
+ | |||
+ | In order to publish your plugin, you can create a GitHub project called '' | ||
+ | |||
+ | Your plugin needs to have a directory called '' | ||
+ | |||
+ | You can create a project that contains more than one plugin, by adding them in the '' | ||
+ | |||
+ | Once you have created the project, you can point to the auto-zip link in GitHub to allow your plugin to be downloaded and installed | ||
+ | |||
+ | < | ||
+ | https:// | ||
+ | </ | ||
+ | |||
+ | Once you've done this, add your plugin to this wiki. This will involve several steps. | ||
+ | - Register as a user of this wiki. | ||
+ | - Register for the PHPlist Developers list at https:// | ||
+ | - Submit a request for write-access to this wiki. Send your request to the PHP Developers mailing list. | ||
+ | - After receiving write-access, | ||
+ | < | ||
+ | Write your page, using some of the source of the other plugin pages as a model. | ||
+ | ====== Multiple plugin locations ====== | ||
+ | |||
+ | To make it easier to develop and test plugins with phplist running locally you can use the PLUGIN_ROOTDIRS constant (note the plural S). This takes a semi-colon separated list of additional directories each of which can contain plugins. This way you can have phpList use a plugin directly in its git repository, instead of having to copy the plugin files to the PLUGIN_ROOTDIR directory. | ||
+ | |||
+ | |||
+ | For example, if you have two plugins under development each with its own git repository then the directory/ | ||
+ | < | ||
+ | / | ||
+ | / | ||
+ | / | ||
+ | / | ||
+ | </ | ||
+ | |||
+ | In this case add a definition for PLUGIN\_ROOTDIRS that identifies the " | ||
+ | < | ||
+ | define(" | ||
+ | </ | ||
+ | |||
+ | |||
+ | phplist will then use these directories in addition to that identified by PLUGIN_ROOTDIR when finding and loading plugins. Now any changes to the files in the git repositories will be used immediately by phplist. | ||