Admin Pack Documentation

Admin Pack is a separate, self-contained, but related product to Barebones CMS. It is an officially recognized product and therefore some kind soul in the support forums will help you if you need it.

Admin Pack is designed specifically for programmers who want to quickly create nice-looking, custom-built administrative interfaces that are secure. My personal experience with Admin Pack is that this tool cuts development time in half and gives me peace of mind about security of the apps I develop. It also looks infinitely nicer and is more user-friendly than my former 1994-esque admin interfaces that used hideous-looking, beveled tables. The difference is night and day. Feel free to laugh now.

This documentation covers everything you need to know about Admin Pack. If you are familiar with the Execution Path of Barebones CMS, a lot of this documentation will be very familiar.

License

Like Barebones CMS, Admin Pack is dual-licensed under a MIT or LGPL license - your choice. The license and restrictions are identical to the Barebones CMS License.

While the license allows for commercial software to be created with Admin Pack, this is really intended for quick-n-dirty, one-site-only admin interfaces. As a programmer who uses this tool, I know I'd be embarrassed if my own quick-n-dirty admins were released to the world. But not nearly as embarrassed as I would be if my 1994-esque ones were released.

Live Demos

The best way to see what can be built with Admin Pack is to take a look at some examples. Here are the available live demos for Admin Pack:

Each demo has full source code available for download. Do not use the demo software for developing your own software other than to see how something was done. The demos tend to lag behind the latest releases of Admin Pack. Follow the instructions below for proper installation and integration of Admin Pack into your environment.

Downloads

Admin Pack 1.0 is the stable release of Admin Pack. Admin Pack Extras 1.0 is the stable release of Admin Pack with Extras.

Download adminpack-1.0.zip

Download adminpack-extras-1.0.zip

If you find Admin Pack useful, please donate toward future development efforts.

Installation

Installation of Admin Pack is fairly straightforward. However, unlike Barebones CMS, there is no fancy installer. In fact, the product is rather useless until a programmer writes code to make it do something. At any rate, the installation procedure is as follows:

A lot of these directions are simplifications of the Barebones CMS Installation documentation.

At this point, the product is "installed" but upon visiting the page, there is very little there. Admin Pack is merely a shell with some minor examples to quickly get you off the ground running. By the way, there are several additional tools on the Download page that are very useful in conjunction with Admin Pack. Specifically, the Ultimate E-mail Toolkit and Ultimate Web Scraper Toolkit. You may also find the self-contained portions of the Barebones CMS itself to be very useful.

Some components of Admin Pack require the Admin Pack extras package to function properly. A good example is the 'date' field type, which uses jQuery UI datepicker to easily select a date. The main tool will display errors when the extras package is required. The extras package adds an additional 350KB or so, which somewhat defeats the purpose of the word "lightweight".

Upgrading

Upgrading Admin Pack is somewhat harder than installation. It depends on the changes that have been made. In many cases, it does not make a whole lot of sense to upgrade. The only reason to upgrade beyond some cool new feature would be security vulnerabilities in Admin Pack itself, but even that should be incredibly rare.

The 'support' directory is usually a matter of overwriting the files located there with those from the latest package. Upgrading 'index.php' may take time to complete depending on the size of the application.

Securing Admin Pack

Administrative interfaces sitting on web servers are a dangerous but somewhat necessary evil. The first thing to do is lock down each Admin Pack instance so only authorized personnel can access the tool. Wouldn't want hackers in there now would we?

Open up 'index.php' in your favorite text editor. If you don't have a favorite text editor, mine happens to be Crimson Editor for Windows. I don't really have a favorite under *NIX-based OSes but some people swear by vim, Emacs, and nano. If I'm editing files on a remote server, I prefer WinSCP over FileZilla because it picks up changes to files and automatically uploads the modified file to the server.

The main 'index.php' file is broken up into three sections: Initialization, security validation, and 'action' processing. The section we are interested in is the security validation section:

// $bb_randpage is used in combination with a user token to prevent hackers from sending malicious URLs.
// [Put random content into the string.  Try www.random.org.]
// https://www.random.org/integers/?num=100&min=0&max=255&col=10&base=16&format=plain&rnd=new
$bb_randpage = "[Random content goes here]";
$bb_rootname = "Tool Name";

// [Put your login management, permissions to access this page, and any obvious initialization logic here.]
// [If you want, you can make any error messages look nice using the BB_GeneratePage() call.]
// [$bb_usertoken should be a string that uniquely identifies the user without directly identifying them.  For example, a session ID.]
$bb_usertoken = "";


BB_ProcessPageToken("action");

Admin Pack reveals its Barebones CMS roots with variable names that have the reserved '$bb_' prefix. This naming convention helps avoid conflicts with your own variable names as you develop the administration interface.

The work that needs to be done here should only take a couple minutes to complete. Most people using Admin Pack already have their own login system written and know how to interface with their system. If you don't have a login system, try the excellent Single Sign-On Server/Client. There are instructions below on how to integrate Admin Pack with the SSO client. The first step is to set $bb_randpage to a randomly generated set of characters. The specified URL in the comment should generate plenty of random data.

$bb_rootname is the global name of the tool and is also used for the title of each page.

$bb_usertoken is the last parameter that needs to be defined. It should be a string that should uniquely identify the user without directly identifying them. Ideally, this is a secret session ID that is never sent to the web browser. However, the current session ID, usually in a cookie, can be good enough.

Example:

// $bb_randpage is used in combination with a user token to prevent hackers from sending malicious URLs.
$bb_randpage = "33f724f3724f2679614855896571bc8c9406355f";
$bb_rootname = "RSS Feed Manager";

require_once "../login/required.php";
if (!$user->admin)
{
	echo "You must be an admin.";

	exit();
}

$bb_usertoken = $user->secretid;

BB_ProcessPageToken("action");

The example may seem simple but I've found that it literally ends up being about that many lines of code to properly secure the administrative interface.

If you are using the Single Sign-On Server/Client, install the SSO client in a subdirectory where Admin Pack resides and use some code like this to secure Admin Pack:

// $bb_randpage is used in combination with a user token to prevent hackers from sending malicious URLs.
$bb_randpage = "33f724f3724f2679614855896571bc8c9406355f";
$bb_rootname = "RSS Feed Manager";

require_once "client/config.php";
require_once SSO_CLIENT_ROOT_PATH . "/index.php";

if (!SSO_LoggedIn())  SSO_Login("", "You must login to use this system.");

// Send the browser cookies.
SSO_SaveUserInfo();

// Test permissions for the user.
if (!SSO_IsSiteAdmin() && !SSO_HasTag("rss_manager"))  SSO_Login("", "insufficient_permissions");

// Get the internal token for use with XSRF defenses.
$bb_usertoken = SSO_GetSecretToken();

BB_ProcessPageToken("action");

There are quite a few similarities to the previous code. If you need certain fields from the SSO server (e.g. e-mail address), be sure to save them with the client to reduce network load times. There are examples in the SSO server/client documentation on how to do that. The forums are available if you get stuck with SSO client integration into Admin Pack.

If you only want users connecting into Admin Pack with SSL, put something like this right after the ProcessAllInput() call:

ProcessAllInput();

// Switch to SSL.
if (!BB_IsSSLRequest())
{
	header("Location: " . BB_GetFullRequestURLBase("https"));
	exit();
}

That detects non-SSL requests and redirects the browser to a HTTPS URL. Depending on how the server is configured, the above code might not work but it should work fine for most hosts.

For the security paranoid, feel free to dig into the BB_ProcessPageToken() function that comes with Admin Pack. It is similar to how the main Barebones CMS editor defends against CSRF/XSRF attacks.

Designing The Menu

The menu in Admin Pack is typically just a bunch of static links to the various features but they can be dynamically generated too. I've personally found that static links work well enough. The menu is comprised of an array that initially looks like this:

// Menu/Navigation options.
$menuopts = array(
	"Temp Title/Section" => array(
		"Some Page" => BB_GetRequestURLBase() . "?action=somepage&sec_t=" . BB_CreateSecurityToken("somepage")
	)
);

Most menus will only have one section and in many cases there will only be a couple pages in the one section. This is, after all, a tool for creating quick-n-dirty admin interfaces.

The important part of the menu is how to define links to pages. Most links will be a name/value pair where the name is the text to display and the value is a string that defines a page to target. Every page should be protected with the security token infrastructure. This is done by calling BB_CreateSecurityToken() with the 'action' of the target page and assigning it to a 'sec_t' variable in the URL.

There may be special cases where the target link needs to do something special (e.g. execute Javascript or open a new window/tab). Instead of assigning a string, assign an array of name/value pairs that identify attributes of an anchor ('a') HTML tag. In practice, I have only needed this feature one time.

Displaying Content

Each Admin Pack 'action' should either display content or process submitted data. When no 'action' is supplied, the default handler is to display a message to pick an option from the menu:

$contentopts = array(
	"desc" => "Pick an option from the left."
);

BB_GeneratePage("Home", $menuopts, $contentopts);

The workhorse in Admin Pack that displays pages is BB_GeneratePage(). It takes a title, an array containing the menu, and an array containing the content to display. It then uses that information to display a complete HTML page. With Admin Pack, there is very little need to write HTML - just fill out the menu and content arrays and let BB_GeneratePage() do the work of writing the HTML.

Behind the scenes of BB_GeneratePage(), the content is processed by BB_PropertyForm(). BB_PropertyForm() takes an array of options and displays a bunch of 'div's and form fields that can be styled easily with CSS. The default layout is generally good enough. If you are familiar with Barebones CMS, BB_PropertyForm() should be very familiar with one exception: This BB_PropertyForm() is not AJAX-enabled and has a few other useful modifications that make it easier to work with in the given context of an administration interface builder.

See the "Core Functions" documentation below for more details on the usage of BB_GeneratePage() and BB_PropertyForm().

Processing Results

When BB_GeneratePage() generates a form for display and it is submitted or a link clicked that causes an action to take place, there needs to be a handler for the action. I generally have forms separated from submission handlers and then redirect somewhere upon completing the task. Here's the default form/submission handler pair that comes with Admin Pack:

if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "somepage")
{
	if (isset($_REQUEST["field1"]))
	{
		if ($_REQUEST["field1"] == "")  BB_SetPageMessage("error", "Please fill in 'Field 1'.");

		if (BB_GetPageMessageType() != "error")
		{
			// [Save data here.]

			BB_RedirectPage("success", "Successfully saved the data.");
		}
	}

	// [Do processing here to generate content options dynamically.]
	$somevar = "default value 2";

	$contentopts = array(
		"desc" => "This is some page.",
		"nonce" => "action",
		"hidden" => array(
			"action" => "somepage"
		),
		"fields" => array(
			...
			array(
				"title" => "Field 2",
				"type" => "text",
				"name" => "field2",
				"value" => BB_GetValue("field2", $somevar),
				"desc" => "Description for Field 2."
			),
			...
		),
		"submit" => "Save",
		"focus" => true
	);

	BB_GeneratePage("Some Page", $menuopts, $contentopts);
}

As can be seen here, the 'somepage' form is targeting itself. This approach allows for error checking and being able to return to the same form for the user to correct any issues.

You could also target another action like 'somepage_submit' and have the target page redirect back to 'somepage' with BB_RedirectPage(). BB_RedirectPage() can redirect anywhere via the function's third parameter but defaults to the value stored in 'bb_back'. The 'bb_back' option can be specified as part of the 'hidden' content options to point to another page. This approach is not really recommended as it becomes more difficult to fill in fields to easily fix errors with user submissions.

Note the 'nonce' parameter. That tells BB_PropertyForm() to use 'action' for the name part of the security token. Any additional hidden parameters are added to another variable called 'sec_extra' and used as part of the complete security token for the form.

Useful Patterns

While developing Admin Pack based software over the past few years, I've settled onto a number of fairly simple patterns that you may find useful while developing your own Admin Pack based solution. First off, I usually dump common functions and globals into a base file, creatively named "base.php" in the same directory as the main admin. This allows me to build out a frontend for it more easily if I need to, since both the admin and the frontend can just include the same base file. I highly recommend using a base file regardless of application size.

Almost all of the admin tools I've built interface with a backend database (e.g. MySQL). I've adopted, almost universally, the data serialization pattern. It performs very well, is flexible, and very easy to work with, cutting development time to a fraction of what it would be otherwise.

For larger projects, I find myself writing configuration files that are serialized data just chucked into a file. That is, serialize()/unserialize() with file_put_contents() and file_get_contents() and just store those values in a $config global variable. Configuration files tend to show up where there is a frontend. Of course, for more serious projects like the SSO server/client, I generate 'settings.php' files so someone can't see what is in the configuration. Regardless, configuration files are useful for allowing other admins to make changes without having to constantly make requests for said changes.

Even with all the tools available, I still end up doing some custom work here and there, but it mostly entails using jQuery to do specific things with specific fields. I tend to output my Javascript in the "htmldesc" part of $contentopts.

However, there are a few specific database related patterns that follow that give me a ton of mileage and help me generally avoid writing HTML.

Database Dump Pattern

If this sounds silly, it probably is. I find myself frequently generating a giant table that dumps out the contents of a database and then giving users the ability to manipulate the database data after locating the entry they are interested in.

This pattern looks a lot like:

<?php
	if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "manageusers")
	{
		$rows = array();
		$result = $db->Query("SELECT * FROM $dbt_users");
		while ($row = $result->NextRow())
		{
			$info = LoadUserInfo($row);

			$rows[] = array(htmlspecialchars($row->name), "<a href=\"" . BB_GetRequestURLBase() . "?action=addedituser&id=" . $row->id . "&sec_t=" . BB_CreateSecurityToken("addedituser") . "\">Edit</a> | <a href=\"" . BB_GetRequestURLBase() . "?action=deleteuser&id=" . $row->id . "&sec_t=" . BB_CreateSecurityToken("deleteuser") . "\" onclick=\"return confirm('Are you sure you want to delete this user?');\">Delete</a>");
		}

		$contentopts = array(
			"desc" => "Below are the users in the system.",
			"fields" => array(
				array(
					"type" => "table",
					"cols" => array("Name", "Options"),
					"rows" => $rows
				)
			)
		);

		BB_GeneratePage("Manage Users", $menuopts, $contentopts);
	}
?>

The surprising thing is that users don't mind dumping entire database tables out to their browser. Within reason. Rows don't seem to matter as much as columns. I tend to only show the most likely columns of information that will be useful to the user. If the user wants to see more information about an entry, they can edit or view the entry.

The mobile-friendly template automatically transforms tables into nice-looking 'div' based output, but it can get to be pretty unwieldy to look at hundreds of rows of data. There's nothing quite like a real desktop and large monitors for working with larger data sets when utilizing this pattern.

Note that this pattern does start to have limitations after a while. Throwing in date range and LIMIT clauses can help, but sometimes building a full-blown search engine is the way to go. It really depends on what kind of data and how much data is involved. For data sets that won't ever exceed a few thousand rows, which covers the vast majority of what people build with Admin Pack, this pattern rocks.

Add/Edit Pattern

This is a relatively new pattern, so I'm still working out the kinks in it, but I find I'm using it extensively and the pattern cuts the amount of code required in half. This pattern combines adding new entries into a database and editing existing entries into a single set of code with only minor differences. If you've worked with Admin Pack before, you have probably written database insertion code and then later wrote database update code. Writing both at the same time saves time and reduces mistakes!

This pattern looks a lot like:

<?php
	if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "addedituser")
	{
		$id = (isset($_REQUEST["id"]) ? (int)$_REQUEST["id"] : 0);
		$row = $db->GetRow("SELECT * FROM $dbt_users WHERE id = ?", array($id));
		if ($row)  $info = LoadUserInfo($row);
		else
		{
			$id = 0;
			$info = InitUserInfo(array());
		}

		if (isset($_REQUEST["name"]))
		{
			if ($_REQUEST["name"] == "")  BB_SetPageMessage("error", "Please fill in 'Name'.");
			else if ($_REQUEST["address"] == "")  BB_SetPageMessage("error", "Please fill in 'Address'.");
			...

			if (BB_GetPageMessageType() != "error")
			{
				$info["name"] = $_REQUEST["name"];
				$info["address"] = $_REQUEST["address"];
				...

				if ($id)  $db->Query("UPDATE $dbt_users SET name = ?, info = ? WHERE id = ?", array($_REQUEST["name"], serialize($info), $id));
				else  $db->Query("INSERT INTO $dbt_users SET name = ?, info = ?", array($_REQUEST["name"], serialize($info)));

				BB_RedirectPage("success", ($id > 0 ? "Successfully updated the user." : "Successfully created the user."), array("action=manageusers&sec_t=" . BB_CreateSecurityToken("manageusers")));
			}
		}

		// Some custom HTML.
		$desc = "";

		$contentopts = array(
			"desc" => ($id ? "Edit the user." : "Add a user."),
			"htmldesc" => $desc,
			"nonce" => "action",
			"hidden" => array(
				"action" => "addedituser",
				"id" => $id
			),
			"fields" => array(
				array(
					"title" => "Name",
					"width" => "35em",
					"type" => "text",
					"name" => "name",
					"value" => BB_GetValue("name", $info["name"])
				),
				array(
					"title" => "Address",
					"width" => "35em",
					"type" => "text",
					"name" => "address",
					"value" => BB_GetValue("address", $info["address"])
				),
				...
			),
			"submit" => ($id ? "Save" : "Create"),
			"focus" => true
		);

		BB_GeneratePage(($id ? "Edit User" : "New User"), $menuopts, $contentopts);
	}
?>

There is a dependency on the aforementioned data serialization pattern. Using that pattern is what makes this pattern possible and further simplifies writing Admin Pack based applications.

Printer-Ready/Summary Pattern

So you've gone digital and like it that way. Your users like dead-tree editions of everything or want a more pleasing format to view entries in. Take a deep sigh and facepalm. This pattern accommodates both the purposes of a printer-ready page and a nicely formatted summary view. You have to have the mobile-friendly layout handy to make it work and there is a lot of custom HTML involved, which generally makes the mobile-friendly layout less mobile-friendly unless you are extra careful.

This pattern frequently looks like:

<?php
	if (isset($_REQUEST["action"]) && $_REQUEST["action"] == "viewuser")
	{
		$id = (isset($_REQUEST["id"]) ? (int)$_REQUEST["id"] : 0);
		$row = $db->GetRow("SELECT * FROM $dbt_users WHERE id = ?", array($id));
		if ($row)
		{
			$info = LoadUserInfo($row);

			// Use the pre-built mobile template.
			require_once "support/mobile_layout.php";

			// Some custom HTML.
			$desc = "";

			$contentopts = array(
				"desc" => "",
				"htmldesc" => $desc,
				"fields" => array()
			);

			$custom = "<style type=\"text/css\">\n";
			$custom .= "div.maincontent div.propmain table.view_main_info td { vertical-align: top; padding-right: 15px; }\n";
			$custom .= "div.photowrap img { border: 1px dotted #CCCCCC; margin-bottom: 0.5em; }\n";
			$custom .= "@media (min-width: 600px) { div.photowrap { float: right; margin-left 2.0em; } }\n";
			$custom .= "</style>\n";
			$custom .= "<table class=\"view_main_info\">\n";
			$custom .= "<tr><td nowrap><b>Name:</b></td><td>" . htmlspecialchars($info["name"]) . "</td></tr>\n";
			$custom .= "<tr><td nowrap><b>Address:</b></td><td>" . htmlspecialchars($info["address"]) . "</td></tr>\n";
			...
			$custom .= "</table>\n";

			$contentopts["fields"][] = array(
				"type" => "custom",
				"value" => $custom
			);

			if ($info["notes"] != "")
			{
				$contentopts["fields"][] = array(
					"title" => "Admin Notes",
					"type" => "custom",
					"value" => "<div class=\"static\">" . str_replace("\n", "<br />\n", htmlspecialchars($info["notes"])) . "</div>"
				);
			}

			BB_GeneratePage("View User - " . $info["name"], $menuopts, $contentopts);
		}
	}
?>

As can be seen, there's not much Admin Pack there. Most of it is custom code containing HTML tables to present the information in a nice, neat format. It helps to know your users so that they don't have to ask for it - you'll already know and can stay two steps ahead of them. The more complex the system, the more likely certain parts of that system will require this pattern.

Creating Alternate Layouts

Admin Pack is designed for administrative interfaces. That said, it can be used for frontends that users see and interact with. However, the default layout most likely does not match your website. Let us assume for some reason you want to go wild and change the default layout to something else. The 'support/page_basics.php' file contains the default layout in the BB_GeneratePage() function:

<?php
// Default layout swiped from the Barebones CMS Layout widget.
// SEO-friendly (2-1) admin-style 2-column pixel-widths liquid layout (200px 100% height, content).
// Sources:
//   http://matthewjamestaylor.com/blog/holy-grail-no-quirks-mode.htm
//   http://matthewjamestaylor.com/blog/ultimate-2-column-left-menu-pixels.htm
if (!isset($bb_page_layout))
{
	$bb_page_layout = <<<EOF
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<title>@TITLE@</title>
<link rel="stylesheet" href="support/admin.css" type="text/css" media="all" />
<script type="text/javascript" src="support/jquery-1.3.2.min.js"></script>
</head>
<body>
<div class="pagewrap">
	<div class="contentwrap">
		<div class="colmask">
			<div class="colright">
				<div class="col1wrap">
					<div class="col1">
						<div class="col1inner">
@MESSAGE@
<div class="maincontent">
@CONTENT@
</div>
						</div>
					</div>
				</div>
				<div class="col2"><div class="leftnav">@MENU@</div></div>
			</div>
		</div>
	</div>
	<div class="stickycol"></div>
</div>
</body>
</html>
EOF;
}

if (!isset($bb_menu_layout))
{
	$bb_menu_layout = <<<EOF
<div class="menu">
	<div class="title">@TITLE@</div>
@ITEMS@
</div>
EOF;
}

if (!isset($bb_menu_item_layout))
{
	$bb_menu_item_layout = <<<EOF
	<a @OPTS@>@NAME@</a>
EOF;
}

if (!isset($bb_message_layout))
{
	$bb_message_layout = <<<EOF
<div class="message"><div class="@MSGTYPE@">@MESSAGE@</div></div>
EOF;
}
?>

The global variables $bb_page_layout, $bb_menu_layout, $bb_menu_item_layout, and $bb_message_layout define the overall layout of the page. When BB_GeneratePage() is called, it checks to see if those variables are already defined. If the variable is not, the default is defined and used. This approach makes it possible to create a single PHP include file from 'index.php' that overrides the layout for all Admin Pack instances - if you plan on creating more than one frontend, this can help with consistently displaying the content.

The global variables $bb_formtables and $bb_formwidths control how tables and widths operate. They normally aren't defined and default to true, which results in the normal behavior. These variables are useful when developing a mobile layout such as the one found in Admin Pack Extras.

Admin Pack Extras

Admin Pack integrates with a related product called Admin Pack Extras. The extras package adds another 350KB to the somewhat large main package. Admin Pack works fine without the extras package but if you use some Extras-specific feature, Admin Pack will complain.

If you try to view Admin Pack on a mobile device with a small screen, the experience gets weird. Admin Pack Extras includes a full example of implementing a mobile-friendly layout suitable for mobile devices and smaller screens. An example layout switcher is included in 'index_mobile.php'. The example also includes an example table to show how tables will display in a mobile scenario.

The extras package also includes support for a date picker and two different multiselect dropdown boxes.

Core Functions

Admin Pack has a number of useful functions available for use. These are automatically included with the 'support/str_basics.php' and 'support/page_basics.php' files.

See the Barebones CMS Global Functions documentation for details about the functions in 'support/str_basics.php' as well as BB_JSSafe(), BB_IsSSLRequest(), BB_GetRequestHost(), BB_GetRequestURLBase(), BB_GetFullRequestURLBase(), BB_Translate(), BB_PostTranslate(), BB_SetLanguage(), BB_InitLangMap(), and BB_PropertyForm() basics.

See the Barebones CMS Extra Components documentation for details on SetCookieFixDomain().

See the Barebones CMS Creating Property Forms documentation for details on BB_PropertyForm(). Note that the 'bb_action' option has been replaced by a combination of 'hidden' and 'nonce' options but the rest of the options are basically identical. There are a couple of new options for 'fields'. If a field is a string instead of the usual array, it can be either 'startrow' or 'endrow' and the fields that fall in-between are table cells. Many field types allow a 'width' to be specified (e.g. '260px') which, when combined, allows for multiple fields to be included on a single row (e.g. City, State, Zip). The default 'somepage' action in 'index.php' has an example of this. There are two additional field types called 'file' and 'date' that are covered below. If Admin Pack Extras is installed, the 'select' field type gains a couple of additional options which are also documented below.

What follows are the remaining core functions and modifications.

BB_FormatTimestamp($format, $ts)

Parameters:

Returns: A translated string containing the timestamp in a human-readable format.

This function runs the output of the PHP date() function through BB_Translate() and BB_PostTranslate(). This function differs slightly from the Barebones CMS function of the same name.

BB_PropertyForm() - Option: "nonce"

A string that identifies a specific key in the required 'hidden' option array that is used as the $name parameter of a BB_CreateSecurityToken() call.

BB_PropertyForm() - Option: "fields", "type" => "select"

Optional:

The 'mode' option is an extra option for this field type that only applies to Admin Pack and requires Admin Pack Extras to be installed and is only utilized when the 'multiple' option has been set to true. When 'mode' is 'flat', a jQuery UI multiselect plugin is used. When 'mode' is 'dropdown', a jQuery UI multiselect widget is used. These modes override the default behavior of generating checkboxes.

BB_PropertyForm() - Option: "fields", "type" => "file"

Required:

This field/element type displays a standard text field and "Browse..." button that selects a file to upload to the server. This replaces the usual file upload mechanism found in Barebones CMS. While it isn't as nice as SWFUpload - that is, no fancy visual feedback while uploading - it is easier to use.

BB_PropertyForm() - Option: "fields", "type" => "date"

Required:

This field/element type displays a standard single-line text field that allows the user to easily select a date.

Note: This field type requires the Admin Pack extras package to be installed in order to function correctly. An alert dialog will display if the package is not installed.

BB_CreateSecurityToken($name, $extra = "")

Parameters:

Returns: A string containing a security token.

This function should be used extensively to generate security tokens to defend against CSRF attacks (the correct phrase is 'security nonce' but the word 'nonce' has inappropriate connotations). Every action, except where an action is not specified, should require a valid security token or an "Invalid security token." message will be displayed.

A security token is based on a mixture of random data plus some regular data and should be on a per-session, per-page, per-hyperlink/form basis. A would-be hacker will find this defense to be very difficult to get around especially since most Admin Pack applications are custom-built.

The $extra parameter is used for creating an even more secure token based on parameters the target routine is expecting to exist. Basically, this feature is overkill. If used, the $extra parameter should be an array of values that gets passed in to include in the security token. When $extra is used, the string 'sec_extra' must also be sent to the server that contains the names of the parameters that were included in $extra. This parameter is automatically used for forms created with BB_PropertyForm().

BB_IsSecExtraOpt($opt)

Parameters:

Returns: A boolean value of true if the option is found, otherwise false.

This simplifies checking the comma-separated string $_REQUEST["sec_extra"] for a specific option. See the description of $extra and 'sec_extra' in the function BB_CreateSecurityToken().

BB_ProcessPageToken($name)

Parameters:

Returns: Nothing.

Checks the security token if $_REQUEST[$name] exists. If it does not exist, it simply returns. The application should also check for the existence of $_REQUEST[$name]. If it doesn't exist, serve up a default page that effectively does nothing.

If the security token is invalid, an "Invalid security token." message is displayed and the program immediately exits.

BB_GetBackQueryString()

Parameters: None.

Returns: A string containing a value suitable for 'bb_back'.

Copies all variables in the $_GET superglobal except for 'bb_msg' and 'bb_msgtype'. The value is then serialized, base64 encoded, and then slightly modified for use in URLs without needing to be encoded any further.

BB_GetBackURL($query = array(), $fullrequest = false, $protocol = "")

Parameters:

Returns: A string containing a URL.

This function is designed to be used for "Back" links and is also called by BB_RedirectPage() to determine where to redirect the browser.

Important: Really long URLs in excess of 2048 characters can potentially be generated with this function. It depends highly on the browser and web server, but there are apparently silly-stupid limits on these critical Internet components. In particular, really deep "Back" link tracking with 'bb_back' could cause problems.

BB_RedirectPage($msgtype = "", $msg = "", $query = array())

Parameters:

Returns: Nothing.

This function exits upon sending the appropriate header to redirect the browser. If the $query option is an empty array, then the call to BB_GetBackURL() uses $_REQUEST["bb_back"] to determine where to redirect the browser to.

When $msg is specified, it should be kept short and to the point as the string is sent as part of the URL. If an empty string is passed for $msgtype when $msg is specified, the default $msgtype becomes 'info'.

BB_SetPageMessage($msgtype, $msg)

Parameters:

Returns: Nothing.

This function sets the message to display when generating the page. Typically used for error messages that get passed through for the user to correct some mistake.

BB_GetPageMessageType()

Parameters: None.

Returns: A string containing one of 'error', 'success', 'info', or an empty string.

This function gets the current message type. Can be used to determine if any errors occurred in previous code.

BB_GetValue($key, $default)

Parameters:

Returns: Either $_REQUEST[$key] or $default.

This function returns either the user-submitted $_REQUEST[$key] or the $default value.

BB_SelectValues($data)

Parameters:

Returns: An array containing value => true pairs.

This function takes a set of values in an array and transforms them into value => true pairs suitable for assigning to a 'select' type's 'select' option.

BB_ProcessInfoDefaults($info, $defaults)

Parameters:

Returns: An array containing key-value pairs.

This function takes two arrays as input and merges the keys and values of $defaults into $info only if $info does not have the key in $defaults defined. Primarily used with the data serialization pattern.

BB_GeneratePage($title, $menuopts, $contentopts)

Parameters:

Returns: Nothing.

Generates the entire HTML page and writes it onto the output stream that gets sent to the browser. See the section above on Creating Alternate Layouts to learn how to change the layout that this function uses to generate the final page.

The approach of calling one function to generate a page helps to create a unified, structured interface. The downside to this approach is if there is a lot of data to write out as it has to be cached until it is written. This can, in turn, consume a lot of memory. However, in most cases, the benefits outweigh this issue. For really large quantities of data, use a custom field and an AJAX callback to fill in that portion of the page. jQuery is included in the default layout, so it should be fairly easy to accomplish this task.

© CubicleSoft