View RSS Feed

Edwin Brown

Widget Programming

Rating: 11 votes, 4.45 average.
by Edwin Brown on Fri 14th Aug '09 at 12:01am (9800 Views)
Widget Programming

Introduction
This is intended for reasonably experienced PHP programmers with some experience in vBulletin programming. You should have at least a basic understand of the vB templating system and phrasing. If you don’t, I strongly recommend you do some homework before trying to program a widget. I will use as an example the static html widget.

It's very early to be writing this, I know. I can't say exactly when alpha testing will start on the CMS system, which will be part of the suite. Soon, but I'm not in a position to give a date. I've had several requests for more information, so here it is. I've attached a zip file for two widgets- the static html and the rss. This may not be the final code, but it's close.

I've walked through the statichtml widget, because it's the simplest. If you understand that, there are two more things you need to know. You can call $this->content->getNodeId() to get the unique id of the page you are on. This allows you to do things like make navigation widgets that are page-dependent. Neither of these widgets need that. Also, most widgets should be cached. There is a generic caching algorithm- the first parameter is a value or array of values that form a key. vB_Cache::instance()->write and vB_Cache::instance()->read() do this for you.

At the risk of repeating myself, let me explain the hierarchy.

In the CMS system you define grids. Grids divide the page into columns, with optional header and sidebar settings.

Then you create widgets. Widgets are a combination of a widget type (i.e. static html), plus a configuration. If you want ten different Youtube videos, that's ten instances of a statichtml type widget. If you want five different Google gadgets, that's five more instances of the statichtml type.

Then you make layouts. Layouts put the primary content into a grid, and optionally place one or more widgets in specific places. You apply a layout and style to each section (section is the primary organization system in the CMS, but not the only one). All the pages in that section get that layout.

Let's say you wanted to have a form-type widget where users could enter information about each article. You could create your own database table and use that. Or you could use the widget storage we designed. In that case you would want to use a per-page configuration. We haven't written any of those, yet, but the setConfig and getConfig calls will handle per-page configuration.

I don't want to take credit for something I don't deserve. I didn't write the CMS system, but I've been working in it a lot for the last couple months. I think you'll find it powerful and well designed for extension.

Code Example

Files

You will need at least two files. You will need to define a package and class. The package should be something unique. The class can be anything you like, but it’s generally something descriptive. You will need to create the following file structure.
<vBulletin directory>
/packages (already there>
/<Your package>
/item
/content
/<your class>
/content
/<your class>
Note that we use all lower case file and path names. The static HTML widget has package = vBCms, class=Static. The files are
/packages/vbcms/item/content/static.php
/packages/vbcms/ content/static.php

Database
You will need to create two database entries to use your widget. In the TABLE_PREFIXpackage table you need to insert a record with package= <Your package>. In the TABLE_PREFIXwidgettype table you need to create an entry with class= <your class>, packageid = <the id from the package table>.

Item/Content file
This file is simple. It’s just for the convenience of the CMS router. You declare a class, and define the package and class. If you have a default configuration setting you should put it here. Here is the file for Static HTML (I’ve removed the comments and licensing.)

<?php if (!defined('VB_ENTRY')) die('Access denied.');

class vBCms_Item_Widget_Static extends vBCms_Item_Widget
{
protected $package = 'vBCms';
protected $class = 'Static';
protected $config = array('html' => 'Enter HTML here.');
}


Content file

This is the file that does the work. You need, at a minimum, to create two functions: getConfigView and getPageView. The first is called to configure your widget, and the second is called to render the view. With most widgets you’ll want to do some caching. For the static html widget it’s one query to get the data, and I couldn’t see executing a query to see if I needed to run one simple query.

Below is the file.



This php file should never be called directly, only from a properly initialized page. This call ensures that.


<?php
if (!defined('VB_ENTRY')) die('Access denied.');
licensing

/*================================================= =====================*\
|| ################################################## ################## ||
|| # vBulletin [#]version[#] - Licence Number [#]license[#]
|| # ---------------------------------------------------------------- # ||
|| # Copyright ©2000-[#]year[#] Jelsoft Enterprises Ltd. All Rights Reserved. ||
|| # This file may not be redistributed in whole or significant part. # ||
|| # ---------------- VBULLETIN IS NOT FREE SOFTWARE ---------------- # ||
|| # http://www.vbulletin.com | http://www.vbulletin.com/license.html # ||
|| ################################################## ################## ||
\*================================================ ======================*/

/**
* Static HTML Widget Controller
*
* @package vBulletin
* @author vBulletin Development Team
* @version $Revision: 31096 $
* @since $Date: 2009-06-03 08:42:31 -0700 (Wed, 03 Jun 2009) $
* @copyright Jelsoft Enterprises Ltd.
*/
Declare the class
class vBCms_Widget_Static extends vBCms_Widget
{
/*Properties======================================= =============================*/

/**
* A package identifier.
* This is used to resolve any related class names.
* It is also used by client code to resolve the class name of this widget.
*
* @var string
*/
Declare the package and class. You need to do this so the data managers and other components know how to handle inputs
protected $package = 'vBCms';

/**
* A class identifier.
* This is used to resolve any related class names.
* It is also used by client code to resolve the class name of this widget.
*
* @var string
*/
protected $class = 'Static';


/*Render=========================================== =============================*/

/**
* Returns the config view for the widget.
*
* @return vBCms_View_Widget - The view result
*/


Here's the first essential function. This creates the configuration interface and saves data. There is a template called vbcms_widget_static_config.

public function getConfigView()
{
Ensure we have been fully loaded. The classes use lazy loading

$this->assertWidget();

Verify and store the form variables we want.
'do' => vB_Input::TYPE_STR,
'html' => vB_Input::TYPE_STR,,
'template_name' => vB_Input::TYPE_STR
));


Get the template view. This rendering of templates is new for the CMS system. You can use the old methods, but this will be a bit faster.

$view = new vB_View_AJAXHTML('cms_widget_config');
Set the title

$view->title = new vB_Phrase('vbcms', 'configuring_widget_x', $this->widget->getTitle());
load the configuration

$config = $this->widget->getConfig();


See if we have data to save. verifyPostId, together with the addPostId function, adds a security layer



if (('config' == vB::$vbulletin->GPC['do']) AND $this->verifyPostId())
{
get the data manager and set the values

$config['html'] = str_replace('%u0025', '%' , vB::$vbulletin->GPC['html']);
if (vB::$vbulletin->GPC_exists['template_name'])
{
$config['template_name'] = vB::$vbulletin->GPC['template_name'];
}

$widgetdm = $this->widget->getDM();
$widgetdm->set('config', $config);


We don't really need this call in this widget. We have the ability to define widgets which have a per-page configuration. For this widget we have a single configuration site-wide. If you want a different statichtml widget you make a new widget instance. We left it here in case we want to extend it later.

if ($this->content)
{
$widgetdm->setConfigNode($this->content->getNodeId());
}
Save it
$widgetdm->save();
Check for errors, and display a message if we need to.
if (!$widgetdm->hasErrors())
{
if ($this->content)
{
$segments = array('node' => $this->content->getNodeURLSegment(),
'action' => vB_Router::getUserAction('vBCms_Controller_Content ', 'EditPage'));
$view->setUrl(vB_View_AJAXHTML::URL_FINISHED, vBCms_Route_Content::getURL($segments));
}

Show we're finished
$view->setStatus(vB_View_AJAXHTML::STATUS_FINISHED, new vB_Phrase('vbcms', 'configuration_saved'));

}
else
{
if (vB::$vbulletin->debug)
{
$view->addErrors($widgetdm->getErrors());
}

// only send a message
$view->setStatus(vB_View_AJAXHTML::STATUS_MESSAGE, new vB_Phrase('vbcms', 'configuration_failed'));
}
}
else
{
Now let's create the configuration interface. First create the view

// add the config content
$configview = $this->createView('config');


if (!isset($config['template_name']) or ('' == $config['template_name']) )
{
$config['template_name'] = 'vbcms_widget_static_page';
}

Add the html code
$configview->html = $config['html'] ? htmlspecialchars_uni($config['html']) : '';

// item id to ensure form is submitted to us
Set the form variables for security
$this->addPostId($configview);

Load the configuration and status

// send the view
$view->setStatus(vB_View_AJAXHTML::STATUS_VIEW, new vB_Phrase('vbcms', 'configuring_widget'));
}
and return the view
return $view;
}




And the second function. This creates the widget.


/**
* Fetches the standard page view for a widget.
*
* @return vBCms_View_Widget - The resolved view, or array of views
*/
public function getPageView()
{
ensure the widget is properly loaded

$this->assertWidget();

get the configuration- in this case it's the html code and the template

$config = $this->widget->getConfig();


if (!isset($config['template_name']) or ('' == $config['template_name']) )
{
$config['template_name'] = 'vbcms_widget_static_page';
}

instantiate the view

// Create view
$view = new vBCms_View_Widget($config['template_name']);


set the class, title, and description

$view->class = $this->widget->getClass();
$view->title = $this->widget->getTitle();
$view->description = $this->widget->getDescription();

load it into the view
$view->static_html = $config['html'];
and return the view
return $view;
}
/*================================================= =====================*\
|| ################################################## ##################
|| # Downloaded: [#]zipbuilddate[#]
|| # SVN: $Revision: 31096 $
|| ################################################## ##################
\*================================================ ======================*/
Submit "Widget Programming" to Digg Submit "Widget Programming" to del.icio.us Submit "Widget Programming" to StumbleUpon Submit "Widget Programming" to Google Email Blog Entry

Updated Fri 14th Aug '09 at 12:36pm by Edwin Brown

Categories
vBulletin CMS

Comments

Page 1 of 3
FirstFirst 1 2 3 ... LastLast
  1. Jose Amaral Rego -
    Jose Amaral Rego's Avatar
    I thought you would have the ability to add some buttons to these 'widget'(s) eg. [Share] <- share link, [Join] <- Goes to signup page and so on.

    This is not what I thought to see at this stage. I was hoping it was going to be more user friendly for everyone.


    My bad, forgot to say thanks what what we are getting now.

    Thanks.
    Updated at by
  2. Edwin Brown -
    Edwin Brown's Avatar
    Quote Originally Posted by Jose Amaral Rego
    I thought you would have the ability to add some buttons to these 'widget'(s) eg. [Share] <- share link, [Join] <- Goes to signup page and so on.

    This is not what I thought to see at this stage. I was hoping it was going to be more user friendly for everyone.


    My bad, forgot to say thanks what what we are getting now.

    Thanks.
    Certainly you can put links. If you just want to add some links, that's a static html widget. If you're not comfortable entering the html to display the links you would use the staticbbcode widget. No programming needed.

    This discussion is for someone who wants to create something completely new. That's where the php programming comes in. Then you would put the buttons into the template, and handle saving and displaying through these functions.
  3. Andreas -
    Andreas's Avatar
    Hmm ... the registration of widget types sounds a bit like a PITA.
    Developers are lazy as we all know (well, at least I am ).

    Therfore I suggest to provide some APIs like

    PHP Code:
    register_widget_type($package$classname);
    unregister_widget_type($package$classname); 
    Or maybe even better: Add productid/volatile to the tables and make assigned widget types auto-register/unregister when installing/uninstalling a product.
  4. Andreas -
    Andreas's Avatar
    ... sorry for doublepost ...
    Updated at by ()
  5. Edwin Brown -
    Edwin Brown's Avatar
    Quote Originally Posted by Andreas
    Hmm ... the registration of widget types sounds a bit like a PITA.
    Developers are lazy as we all know (well, at least I am ).

    Therfore I suggest to provide some APIs like

    PHP Code:
    register_widget_type($package$classname);
    unregister_widget_type($package$classname); 
    Or maybe even better: Add productid/volatile to the tables and make assigned widget types auto-register/unregister when installing/uninstalling a product.
    The problem with this is that until the widget type is in the database, the widget type isn't available in the admin panel. So the auto-register code can't be run until after the widget type is registered.

    The point of entering the record in the database is that the admin panel has to have some way to know what widgets are available. We could write an api to do it- it would be trivial- but it would save exactly one line of code. You'd just exchange two database insert queries with a single API call. The current approach is a standard step in installing a plugin, and we didn't want to unnecessarily invent something new
  6. Dismounted -
    Dismounted's Avatar
    Thanks for the (small?) insight into widgets - I look forward to working with them.

    However:
    1. There seems to be a mixture of camelCase and underscoring. Why is this so and which one should be used in which cases?
    2. I see you're using "Static" as the class identifier. This could be confused with the PHP keyword. Probably a smarter move to name it "StaticHtml" or something to that effect.
  7. Fusion -
    Fusion's Avatar
    Great blog, Edwin!
  8. ArcVox -
    ArcVox's Avatar
    In gold version, will function & method names follow http://www.vbulletin.com/docs/html/c...unction_naming ?

    Custom function names should be all lower-case and should use underscores to separate words.
  9. eXaulz -
    eXaulz's Avatar
    Very cool. But I have a question: what's the name of the Array that is first set in getConfigView()?

    public function getConfigView()
    {
    Ensure we have been fully loaded. The classes use lazy loading


    $this->assertWidget();

    Verify and store the form variables we want.
    'do' => vB_Input::TYPE_STR,
    'html' => vB_Input::TYPE_STR,,
    'template_name' => vB_Input::TYPE_STR
    ));
  10. Dismounted -
    Dismounted's Avatar
    Edwin probably chopped off some code when he added the blue description text. The "full" code is:
    PHP Code:
            vB::$vbulletin->input->clean_array_gpc('r', array(
                
    'do'      => vB_Input::TYPE_STR,
                
    'html'    => vB_Input::TYPE_STR,
                
    'template_name'    => vB_Input::TYPE_STR
            
    )); 
    It's interesting to see vBulletin's new structure. Also good to see some nice clean code from the new team.
  11. Edwin Brown -
    Edwin Brown's Avatar
    Quote Originally Posted by Dismounted
    Thanks for the (small?) insight into widgets - I look forward to working with them.

    However:
    1. There seems to be a mixture of camelCase and underscoring. Why is this so and which one should be used in which cases?
    2. I see you're using "Static" as the class identifier. This could be confused with the PHP keyword. Probably a smarter move to name it "StaticHtml" or something to that effect.
    The mixture of naming types is historical. We're now using camelcase for new functions, but until recently underscores were the standard.

    Good point about "static". I'll see if I can rename it without causing too much trouble.
  12. Dean C -
    Dean C's Avatar
    Can you please edit this post, putting the PHP code inside php tags Edwin ?
  13. Edwin Brown -
    Edwin Brown's Avatar
    Quote Originally Posted by Dean C
    Can you please edit this post, putting the PHP code inside php tags Edwin ?
    Good catch. The second file always had the php tag, but in removing the comments from the first I removed one line too many. I just added. I removed comments from the first file, because there's so little code it's hard to see it among the comments and licensing.
  14. davide101 -
    davide101's Avatar
    This looks great to me. It's very exciting to see the flexibility that will come with the widgets. Great work!
  15. David Grove -
    David Grove's Avatar
    Thank you for the post Edwin.
  16. ThorstenA -
    ThorstenA's Avatar
    Thanks for your blog post, Edwin!
  17. Razasharp -
    Razasharp's Avatar
    Thanks for the info Edwin!
  18. Revanza -
    Revanza's Avatar
    It's a shame camelCase is the new standard, I much preferred underscores

    Good post though, looking forward to programming my site with this
  19. Dody -
    Dody's Avatar
    I am glad we are able to create our own code.
    I have a question:
    I already have a well-coded php script that pulls data from an external database, is it possible to place the php script inside the cms folder withut creating the package/class above? or do I need to insert it into a class as you described above?
  20. Ryan Ashbrook -
    Ryan Ashbrook's Avatar
    Quote Originally Posted by Revanza
    It's a shame camelCase is the new standard, I much preferred underscores

    Good post though, looking forward to programming my site with this
    Depends on the product/programmer.

    My mods for vB4 will be using underscores.
Page 1 of 3
FirstFirst 1 2 3 ... LastLast
Total Trackbacks 0

Trackbacks

Trackback URL: