NewsFeaturesDownloadsDevelopmentSupportAbout Us
User Interface Guidelines

User Interface Guidelines

From LifeType Wiki

Contents

[edit] Introduction

We have spent a great amount of time trying to come up with an easy to use, attractive and standards-compliant user interface, and also making sure that all pages and screens in LifeType look and behave in exactly the same way. Therefore, we feel that any plugin that wishes to integrate well with the rest of the admin interface should follow the same design guidelines so that users do not notice any difference between features added via plugins and core features, which in the end, is the goal of plugins.

Plugins released through the project page must comply with these guidelines or else they will be rejected.

[edit] Localization

It is strongly recommended to at least provide an English translation for your plugin and set it as the default language. Remember that if users have configured their blogs in a language that your plugin does not support, LifeType will use the first language specified in the PluginBase::locales attribute. There is a big usability difference between this code:

function PluginSamplePlugin()
{
  $this->locales = Array( "nl_BE", "en_UK" );
}

and the following code:

function PluginSamplePlugin()
{
  $this->locales = Array( "en_UK", "nl_BE" );
}

The difference is that if a user is using Chinese in his/her blog, in the first case since the plugin does not support the zh_CN locale, LifeType will choose Dutch as the display language for the plugin. In the second case, English will be used and chances are that our Chinese-speaking user will be able to understand a bit more English than Dutch.

Whenever developing a new template for your plugin please try not to hardcode any text into the template itself as it will make things easier for other people to translate it to other languages if you cannot do it yourself.

If English is not your mother tongue and you do not speak it very well, do not panic, ask for help.

Also, try to reuse as many strings from the standard locale file instead of adding your own. The smaller your locale files are, the faster LifeType wil be able to load them and merge them with the current language.

[edit] Different pages

In LifeType's administrative interface, there are two clear situations when designing pages: either our page shows a list with data or it is an input form where we expect some input from the user. There is a third situation where our page is a list with data and an input form but this situation will be handled as a list, since the list probably is the most important bit of the page.

[edit] Lists

Standard lists are always displayed as HTML tables. Tables must always have a header section where the headers of each column are specified as <th>...</th> tags.

The total width of a table in LifeType must be exactly 775 pixels, and the right CSS class to user for tables is info.

The heading of a table must contain the following HTML code:

 <table class="info">
  <thead>
   <tr>
    <th style="width:10px;"><input class="checkbox" type="checkbox" class="check" name="all" id="all" value="1" onclick="toggleAllChecks('yourCheckboxes');" /></th>
    <th style="width:400px;">your-first-column-heading</th>
    <th style="width:170px;">your-second-column-heading</th>
    <th style="width:100px;">your-third-column-heading</th>
    <th style="width:95px;">actions-column</th>
   </tr>
  </thead>
  <tbody> 

Widhts should always, unless strictly necessary, be specified as pixels for better control of the layout.

If users are allowed to select items from your list, the first column must be a checkbox to indicate that an item has been selected. Also, the first heading of the table should include the following code:

<th style="width:10px;">
 <input class="checkbox" type="checkbox" class="check" name="all" id="all" value="1" onclick="toggleAllChecks('yourCheckboxes');" />
</th>

toggleAllChecks('id') is a javascript function that will automatically select all the items from the table when the checkbox from the heading is ticked. Please make sure to use the right DOM object identifier as the parameter.

If your table does not allow users to work with items, this can be ignored.

If your table allows users to work with items from your list (edit them, delete them, etc) the last column of your table should be called "Actions" (use {$locale->tr("actions")} to get the correct localized translation) and should allow users to act on each item individually. So for example if your table allows users to remove items from it and to edit certain items, the "Actions" column should include an icon for each one of these actions. One good example is the "Posts" page where a post can be edited, removed and its statistics can be checked:

<td>
  <div class="list_action_button">
    <a href="?op=editPost&postId=198"><img src="imgs/icon_edit-16.png" alt="edit" /></a>
    <a href="?op=deletePost&postId=198"><img src="imgs/icon_delete-16.png" alt="Delete" /></a>
    <a href="?op=postStats&postId=198"><img src="imgs/icon_stats-16.png" alt="statistics" /></a>
  </div>
</td>
All the possible actions should be enclosed in a
block using the list_action_button class. The list of available icons to represent the actions can be found in the imgs/ folder (icon files are named icon_*.png), but if there is not a suitable icon already available, new ones can be added.

[edit] Filtering data

If our list allows to filter the data using several different criteria, we should display a box above the table with all possible options. The box is created with the following code:

    <div id="list_nav_bar">
       <div id="list_nav_select">
           <form id="showBy" action="admin.php" method="post">
               <fieldset>
                  <legend>{$locale->tr("show_by")}</legend>
                   ...
                   <div class="list_nav_option">
                    <br />
                    <input type="hidden" name="op" value="yourActionCode" />
                    <input type="submit" name="Show" value="{$locale->tr("show")}" class="submit" />
                    </div>
                </fieldset>
                </form>
            </div>
            <br style="clear:both;" />
        </div>
The main form is enclosed within two
blocks that will help separate the filters from the rest of the page. The button that triggers the action to update the list with the new filters should be the last item on the right of all other items and should be called "Show" (use the $locale->tr() method to get the right translation.

For each one of the individual criteria, this is how the XHTML code should look like:

   <div class="list_nav_option">
       <label for="exampleParameter">{$locale->tr("example_parameter_title")}</label>
       <br />
       <select name="exampleParameter" id="exampleParameter">
           <option value="1">{$locale->tr("whatever")}</option>
            ...
        </select>
   </div>
Every option is enclosed within a
block that uses the "list_nav_option" class. Please do not apply any custom styling unless strictly necessary.

Please do not omit things like the <label> tag, as they are needed for usability reasons.

[edit] Paging data

In case our list allows paging of the data (show data in different pages to reduce the length of the list), our view class must have defined an object called $pager of type Pager with the right parameters. Once the object has been created and passed to the template renderer, it can be easily displayed using Smarty:

{include file="$admintemplatepath/adminpager.template" style=list}

The style parameter indicates which style of pager we would like to use. As of LifeType 1.0, there are two different values:

  • list: shows a drop-down list of the pages, with a left arrow to go to the previous page and a right arrow to go to the next page. When selecting one of the pages from the list, users will be taken right away to the page via Javascript. It is strongly recommended to use this type of pages.
  • links: shows a horizontal list of links and the current page is highlighted.

[edit] Input Forms

Input forms in LifeType have also been standardized to a great extent to ensure that users only see one way of entering data accross the whole interface.

Form fields should be enclosed within <fieldset> tags, and each fieldset should have its own <legend>...</legend> tag:

 <form name="yourFormName" method="post" action="admin.php">
  <fieldset class="inputField">
   <legend>{$locale->tr("yourTitle")}</legend>
    ...
  </fieldset>
  ...

The right CSS class to use with forms is inputField.

Regarding separate fields, they should be defined as follows:

   <div class="field">
    <label for="categoryName">{$locale->tr("field_name")}</label>
    <span class="required">*</span>
    <div class="formHelp">{$locale->tr("field_name_help")}</div>
    <input type="text" value="{$someValue}" id="fieldName" name="fieldName" />
    {include file="$admintemplatepath/validate.template" field=fieldName message=$locale->tr("field_error_message")}
   </div>
Each one of the fields should have a short name assigned to it using the <label> tag, but do not use the field name to give a description of what the field does. Instead, use the
...
line to give a description of the purpose of the field.

It is important not leave out the <label> tag as it is needed for usability reasons. Also, do not forget the "id" attribute from the <input> field as according to the HTML specification, the "for" attribute should refer to the value of the "id" attribute of the input field described by the label.

Users should notice if a field is mandatory because of a bold asterisk "*" symbol on the right of the field name. In order to specify that a field is mandatory, use the code as defined above:

 <span class="required">*</span>
Every field must have some text describing what it does. The information about the field should never be included in the <label> attribute but just below it. Enclose the field help in a
attribute using the formHelp CSS class:
    <div class="formHelp">{$locale->tr("field_name_help")}</div>

The field itself should be define below the label and the field description text.

Submit buttons should be placed outside the <fieldset> tags on the bottom right corner of the page. The submit button (or the button that carries out the main action) must be on the rightmost part of the page (as in Mac OS X or the Gnome desktop environment) Buttons of lesser importance should be laid out from right to left (from most important to least important) Whenever possible, please try to provide a "reset" button so that users can undo all the changes so far in the form.

A sample of correctly laid-out buttons:

  </fieldset>
  <div class="buttons">
    <input type="reset" name="reset" value="{$locale->tr("reset")}"/>
    <input type="submit" name="Add User" value="{$locale->tr("add")}"/>
    <input type="hidden" name="op" value="addUser" />
  </div>
 </form>

Finally, avoid adding
as much as you can, specially if you're trying to use them to separate fields. The spacing between fields is already good enough and there is no need to increase it!

[edit] Form and Data validation

When dealing with forms for data input, we might run into situations where the data entered by the user (or lack thereof) is not correct or does not have the right format. To deal with situation, first of all we should adapt our custom actions to use the form validation framework as described in LifeType 1.0/Forms and data validation

If some of the data has been validated not to be correct, we have to find a way to report it to the user so that the data can be corrected.

First of all, we should show a negative feedback message on the top of the form with a generic error message. As an example:

<form name="addUser" action="admin.php" method="post">
  <fieldset class="inputField">
    <legend>{$locale->tr("createUser")}</legend>
    {include file="$admintemplatepath/formvalidate.template" message=$locale->tr("error_adding_user")}

The Smarty code included in templates/admin/formvalidate.template will handle error situations and will show the error message only if necessary.

We must also inform users about which fields were wrong and what the error was:

    <input type="text" value="{$someValue}" id="fieldName" name="fieldName" />
    {include file="$admintemplatepath/validate.template" field=fieldName message=$locale->tr("field_error_message")}

The file templates/admin/validate.template will take care of handling data validation issues but it needs two parameters:

  • field: This is the name of the field that is being checked for validation.
  • message: This is the error message that will be shown in case the data in the field is not valid. The message should be descriptive enough, but make sure that none of the standard error messages is enough before addding new messages.

[edit] Feedback

There usually are two types of feedback: positive feedback and negative feedback:

  • Positive feedback should be used whenever we want to inform the user about a successful or harmful operation. When using LifeType's standard mechanisms for reporting positive feedback, text will be displayed in green colour.
  • Negative feedback should be shown whenever an error occurred. When using LifeType's standard mechanisms for reporting negative feedback, text will be displayed in red colour.

However, keep in mind that positive feedback and negative feedback are not mutually exclusive. Some operations can potentially report positive and negative feedback at the same time specially in those cases when users are allowed to operate on more than one item at the same time. As an example, take the "Categories" page: users are allowed to remove more than one article category at the same time by selecting any of them and clicking the "Delete" button.

During the process, it might be that some of these categories have articles categorized under them and therefore they will not be removed while some others will be removed just fine. The outcome of the operation will be a mix of positive and negative feedback that our pages should be ready to show.

[edit] Positive Feedback

First of all, in order to give positive feedback to users you must avoid AdminMessageView. This view is still available but it has been deprecated and it will be removed from future versions.

The most important reason not to use this view is that it will correctly show the feedback message but in a "contextless" page. Pages generated by this view lack the tab on the top and the navigation aids on the top right corner of the page. Also, pages do not automatically generate any "back"-like links to return to the page where the process was started so from a usability point of view, it might be very confusing for users.

Messaages should be shown to users in the same page to the action was started, or in a page where it makes sense to do do. For example if we are dealing with a list that allows users to remove items from it, the positive feedback message should be shown just on top of the list and informing users about how many items were removed.

From a PHP code point of view, this is not acceptable anymore:

 $this->_view = new AdminMessageView( $this->_blogInfo );
 $this->_view->setMessage( $this->_locale->tr("your_feedback_message_id"));

and it should be replaced with

  $this->_view = new AdminYourCustomView( $this->_blogInfo );
  $this->_view->setSuccessMessage( $this->_locale->tr("your_feedback_message_id"));

If you do not have a custom view, you can replace AdminYourCustomView with AdminPluginTemplatedView and specify the name of a template file that will be used to render the contents.

Once the view knows that it has some positive feedback message to show, we need to find a place in the template file to show it. In order to do so, add the following bit of Smarty code anywhere in your file:

{include file="$admintemplatepath/successmessage.template"}

successmessage.template is a file that can be found under templates/admin and that includes all the necesary code to show standard feedback messages.

[edit] Negative Feedback

In the same way that AdminMessageView should be avoided, AdminErrorView should be avoided too for the same reasons. Our PHP code should be modified:

 $this->_view = new AdminYourCustomView( $this->_blogInfo );
 $this->_view->setErrorMessage( $this->_locale->tr("your_error_message_id" ));

Similarly to what is done with positive feedback messages, our templates should be modified to include the following Smarty code to show the message:

{include file="$admintemplatepath/errormessage.template"}

[edit] Where to show feedback

Feedback should not be shown anywhere in a page. Using the two situations described below:

  • Lists: Feedback should be shown just above the list. If our list also includes filtering criteria, feedback messages should be shown between the filtering criteria and the top of the list.
  • Input forms: feedback messages should be shown inside the <fieldset>...</fieldset> tags above every other field and right below the <legend>...</legend> pair of tags.

[edit] Adding new menu entries

Using the new LifeType 1.0/The Menu API it is possible for plugins to add new sections to the admin interface for better integration. However, plugin developers must be careful not to create too many useless entries that might complicate too much the structure of the interface.

  • A first level entry is a new menu entry that would be displayed on the top menu bar. Unless strictly necessary, plugins must not add any entry there as the top bar is alreay quite crowded.
  • A second level entry is an entry that would be displayed in its own page and as a result of clicking one of the first level entries from the top menu bar. Usually second level entries will only contain further third level entries and should not display any contents of their own. An example of second level entries could be "Manage Links", "Manage Posts", etc.
  • A third level entry is an entry that "hangs" from a second level entry and that display content of their own. Third level entries are also displayed in tab groups just below the navigation section in the admin interface.

It is left up to the plugin developers where to create their new entries but the following are a few useful guidelines:

  • Do not create fourth level entries as the interface is not able to cope with them. Three-level deep menu structures should be more than enough anyway.
  • Do not add further second level entries if you can use one of the existing ones. If your plugin configuration page can be added to "/menu/controlCenter/manageSettings", do not create your own "/menu/controlCenter/myPluginSettings". However, be aware the that current tabbed display only allows for about 8-10 tabs to be comfortably displayed. Any more than that will make the tabs wrap and the display will look completely garbled.
  • Use Menu::entryExists($entryPath) to check if a menu entry already exists. For example several different plugins could use "/menu/controlCenter/pluginSettings" to show their configuration pages but since we do not want each one of them to create a new entry, plugins should check whether the entry already exists. If it does exist, then do nothing and just register their new entry. If it doesn't then it will have to be created.

[edit] Choosers

Choosers are a new UI component introduced in LifeType 1.1 that allows users to make a selection and get the values of the selection to be brought back to a field or list.

Currently LifeType 1.1 offers two general-purpose choosers:

  • Blog chooser
  • User chooser

It is advisable to use these components if your customizations or plugin require users to select any of the items above.

There are three additional choosers available in the class/action/admin/chooser/ folder but those are not meant for general usage and therefore irrelevant from a point of view of customizing LifeType.

These choosers appear in a pop-up window and via Javascript, they set their result to a field that has a pre-defined "id" attribute. Additionally, the blog chooser and the user chooser also have a multiple mode that allows multiple selections to be made.

[edit] User chooser

In order to call the user chooser, open it in a new pop-up window with the following parameters:

<a href="#"
onclick="window.open('?op=siteUsersChooser','UserChooser','scrollbars=yes,resizable=yes,toolbar=no,height=450,width=600');">
Select User
</a>

The size of the window and its location is not the most important thing, but the parameter ?op=siteUsersChooser is.

In order for this chooser to set its selected value back into the calling window, there must two textfields, one for the user id and one for the user name:

  • A textfield whose id attribute is userId, that will be used to store the user id.
  • A textfield whose id attribute is userName, that will be used to store the user name.

Depending on the needs of the calling page, userId, or userName or both might be set to hidden or visible.

<input id="userId" name="userId" value="" type="hidden">
<input id="userName" name="userName" value="" type="text">
<a href="#" onclick="window.open('?op=siteUsersChooser','UserChooser','scrollbars=yes,resizable=yes,toolbar=no,height=450,width=600');">
Select User
</a> 

In multiple mode, the selection from the chooser is added to a list instead of to a single field. In order to activate multiple mode, the URL that calls the chooser is exactly the same and the only difference is the mode parameter whose value is now 2:

<a href="#"
onclick="window.open('?op=siteUsersChooser&mode=2','UserChooser','scrollbars=yes,resizable=yes,toolbar=no,height=450,width=600');">
Add User
</a>

As destination of the chooser now there should be a <select> field whose id is userList. Every time a new item is added to the list, the value attribute will be the user id and the string displayed in the list will be the user name:

<select id="userList" name="blogUsers[]" size="10" multiple="multiple" style="width: 40%;">
</select><br/>
<a href="#" onclick="window.open('?op=siteUsersChooser&mode=2','UserChooser','scrollbars=yes,resizable=yes,toolbar=no,height=450,width=600');">Add</a>

[edit] Blog chooser

The blog chooser works in exactly the same way as the user chooser, the only difference being the URL that calls the chooser.

In simple mode the following code would call the chooser, set the blog id to a hidden field called blogId and set the blog name to a visible field called blogName:

<input id="blogId" name="blogId" value="" type="hidden">
<input id="blogName" name="blogName" value="" type="text">
<a href="#" onclick="window.open('?op=siteBlogsChooser','BlogChooser','scrollbars=yes,resizable=yes,toolbar=no,height=450,width=600');">
Select Blog
</a> 

In multiple mode:

<select id="blogList" name="blogs[]" size="10" multiple="multiple" style="width: 40%;">
</select><br/>
<a href="#" onclick="window.open('?op=siteBlogsChooser&mode=2','UserChooser','scrollbars=yes,resizable=yes,toolbar=no,height=450,width=600');">Add</a>