As I wrote back in February, one of the big changes coming to pfSense® is a conversion of the webGUI to Bootstrap. Sjon Hortensius has recently accepted some 51 pull requests on his ‘bootstrap’ branch of the pfsense repo. As you can see from that page, a large number of these come from Steve Beaver, a Netgate® employee.
Converting the pfSense GUI to bootstrap is an interesting process partly because there are so many options. You can simply change the class of the existing display elements to make the page look “Twittery”, you can employ an entirely new PHP object-oriented framework to largely eliminate HTML all together, and you can replace existing JavaScript to more modern DOM based JS. Like an oil painting, it can occasionally be difficult to know when to stop.
In general, the current philosophy is to use the O-O framework where it makes clear sense (such as a page that consists largely of forms), to convert any display elements outside of the forms by changing the element classes, and to leave the JavaScript mostly as-is.
In some cases there are obvious improvements to the code organization that can be made. Large chunks of PHP embedded in HTML can be moved to a function, making the HTML cleaner and easier to read. This also goes some way towards separating the view from the model, but this is perhaps a goal for a future project.
More information on the O-O framework, and on the conversion process can be found at the SjonHortensius bootstrap project.
In general, we have found that following this simple list produces some very nice looking pages.
- Run the clean.sh script on the file to strip unneeded/deprecated HTML
- Remove the extraneous tags from the end of the file
- Where a table is used as the main display container, replace this with a panel (The current code uses a lot of tables within tables. Tables within panels tend to work better in bootstrap) for example
- Remove all deprecated table attributes (valign, align, width=100% etc)
- Add consistent bootstrap table attributes e.g.: class=“table table-striped table-hover table-compact”
- Add bootstrap button classes to all buttons/input.
- Use bootstrap typography decoration in place of , etc
- Use bootstrap alert messages (alert-warning, alert-danger, alert-success etc) when feedback is required
- Add DEBUG conditionals where helpful (e.g.: If you click the reboot button, you don’t really want the box to reboot while you are in the middle of development work so wrap the function call in an if(DEBUG))
- Remove any redundant tags.
- Fix any logic errors or bugs encountered (e.g. You can only add an alias from the DNS page if other aliases already exist)
- Make the indenting/tabs/trailing whitespace etc consistent if you have an editor that can do this for you.
- When there are a lot of conditionals in the (Elements that only display if certain configuration items are present) simulate those configuration items and wrap them in if(DEBUG)
- Write a unit test document so that those who come after you will know if they break something (or if you have)
- Use the test document to unit test your work thoroughly paying particular attention to view-port (browser window) size. Does the page look as good on a smartphone, tablet, laptop, IMAX theatre?
- Finally, keep it small! There is a tendency for Bootstrap styled pages to grow very large and for information that could once be seen on a single screen to require scrolling though many screens. Keep padding to a minimum, use condensed, small, or extra-small elements where possible, and try to get more elements onto each line rather than one line per element.
Building a page using the pfSense PHP framework is simple and clean. In many cases, not one line of HTML is required, although this is always an option when needed.
The advantages of using the framework include consistency, the ability to control the entire web from a few simple framework files, and a clean, easy to understand design. Note that internationalization via gettext()
is handled within the framework, as is htmlspecialchars()
where required. You no longer need to use those.
Here is an example of how to build a simple form with a checkbox, a text box, a selector, and both submit and cancel buttons.
require('classes/Form.class.php');
// The constructor for Form automatically creates a submit button. If you want to suppress that
// use Form(false), of specify a different button using Form($mybutton)
$form = new Form();
// Create a new form section and give it a title
$section = new Form_Section('General Logging Options');
// Add a checkbox to our new form section
$section->addInput(new Form_Checkbox(
'reverse', // checkbox name (id)
'Forward/Reverse Display', // checkbox label
'Show log entries in reverse order (newest entries on top)', // checkbox text
$pconfig['reverse'] // checkbox initial value
));
// Add an input text box
$section->addInput(new Form_Input(
'nentries', // Name
'GUI Log Entries', // Label
'text', // type (could also be 'hidden' or . . )
$pconfig['nentries'], // Initial value
['placeholder' => '0 .. 4096'] // placeholder text
))->setHelp('This is only the number of log entries displayed in the GUI. It does not affect how many entries are contained in the actual log files.');
// Add a selector (option) box passing the selector values in an associative array
$section->addInput(new Form_Select(
'filterdescriptions',
'Where to show rule descriptions',
$pconfig['filterdescriptions'], // Initial value. If it matches on of the selectors it will be highlighted
array(
'0' => 'Dont load descriptions',
'1' => 'Display as column',
'2' => 'Display as second row'
)
))->setHelp('Show the applied rule description below or in the firewall log rows' . ' ' .
'Displaying rule descriptions for all lines in the log might affect performance with large rule sets');
$form->add($section); // Add the section to our form
// We need a 'Cancel' button. We will create it explicitly to demonstrate how to change its appearance
$btncncl = new Form_Button(
'cancel', // name
'Cancel', // Label text
$referer // Optional link
);
// Change the class from the Bootstrap default to 'info', a nice shade of pale blue and make it small
$btncncl->removeClass('btn-default')->addClass('btn-info btn-sm);
// Add it to our form
$form->addGlobal($btncncl);
// Finally . . We can display our new form
print($form);
We’ve also focused on usability and responsive design. As an example, here is the State Table summary as viewed on a desktop:
and here is the same page, displayed on a phone:
And, just to show the degree of care that motivates us, here is a different project’s version of the same page:
Notice the horizontal scroll-bar and table cut-off. Their table is not fluid or responsive.
The edit.php
file was the one most challenging pages to convert, but the effort was made to fix it so that it worked in a Bootstrap environment. The code was just too nicely done to discard, and the result works perfectly.
There are some pervasive changes we’ve made to the system in the process of converting the webGUI. The js library that was used to draw pie charts (portrait) had to go. It is compatible with neither jQuery or bootstrap. After careful deliberation, we chose to use a library called d3pie. It’s BSD-licensed and it works very well. Here is an example of the result. Notice how the pie segments slide out when clicked.
and, again, just to show how we view it as necessary to actually complete the work, rather than ship a half-effort, die Konkurrenz (and no, this isn’t a wry joke, theirs really is blank):
At this point, the conversion process is 88% complete, and we anticipate shipping the updated webGUI, along with the elimination of PBIs (replaced by FreeBSD’s pkg(ng) in pfSense software version 2.3. Notice that in the truest spirit of Open Source, all development has taken place in the open. We don’t hide, and we take the time to do it right.