Search
Widgets
As stated in previous posts it is possible to extend the default Ninja functionality by creating widgets. In fact, we have included some widgets as default. They are primarily used in the Tactical Overview but the plan is to make it possible to add widgets to almost any page. By using a functionality similar to <a target="_blank" rel="nofollow" href="http://www.google.com/ig" target="_blank">iGoogle</a> it will be possible to move the widgets around, edit settings etc. All this is currently pretty much at design stage but it looks really promising. Today we are using the widgets as hard-coded, self-contained components on a page but this should change in a not so far future.<br /><br />So how do you build a widget?<br />First of all - consider this a draft. We are still finding our way and input is always appreciated.<br /><br />A widget is instantiated using the widget helper:<br /><code><br />widget::add('netw_health', array('index', $this->model), $this);<br /></code><br />The helper takes care of a lot of the backend stuff like handling paths and assigning master controller variables. More on this later.<br /><br />Why do we use a helper and not a controller?<br />Since kohana fires off some system events (like system.post_controller) every time a controller is finished, we didn't want this overhead. Also, we didn't want the widget output to be returned to browser immediately but wanted the master (calling) template to decide where to render the output. Our solution was instead to use a helper class and buffer the output using <code>ob_start()</code> and <code>ob_get_contents()</code>.<br /><br />The widgets has to follow <span style="font-weight:bold;">some simple rules</span> to work:<br /><a target="_blank" rel="nofollow" onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_cu2YE9iTvlY/SdXYz1RJOhI/AAAAAAAABPY/1QzGao9TkSs/s1600-h/widget.png"><img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 154px; height: 125px;" src="http://4.bp.blogspot.com/_cu2YE9iTvlY/SdXYz1RJOhI/AAAAAAAABPY/1QzGao9TkSs/s320/widget.png" alt="" id="BLOGGER_PHOTO_ID_5320396919792024082" border="0" /></a>It has to be placed in a folder with the exact name as the widget.<br />The folder should be in <code>widgets</code> or - if you are developing your own - in <code>custom_widgets</code><br />The widget class should be named like the widget name + _Widget and should extend widget_Core. If we look at the netw_health widget as an example:<br /><pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code>class Netw_health_Widget extends widget_Core<br />{<br /> //<br />}</code></pre>Everything is case sensitive here.<br /><br />Our widget also needs a <span style="font-weight:bold;">constructor</span>:<br /><pre name="code" class="php">public function __construct()<br />{<br /> parent::__construct();<br /><br /> # needed to figure out path to widget<br /> $this->set_widget_name(__CLASS__, basename(__FILE__));<br />}<br /></pre><br />The constructor sets the name and path needed for the master controller to be able to render the widget correctly.<br /><br />To look at our example again:<br /><code>widget::add('netw_health', array('index', $this->model), $this);</code><br /><br />The second argument to widget::add() is an array and the first element should always be the method to call. Some widgets will need access to some data to be useful and in this case we are passing a reference to the model used in the master controller.<br />The last argument here is the reference to the master controller so that we are able to pass the generated widget content. The master controller takes care of putting these at the appropriate place in the rendered HTML head.<br /><br />Our <span style="font-weight:bold;">index method</span>:<br /><pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code>public function index($arguments=false, $master=false)<br />{<br /> $this->master_obj = $master;<br />}<br /></code></pre>This first assignment is important since it is how we refer back to the master controller - without it, the widget won't be visible anywhere.<br /><br />The widgets view file could be called anything (as long as it has a php extension) and to use it you call <code>$view_path = $this->view_path('view');</code> with the name of the view template as an argument.<br /><br /><span style="font-weight:bold;">The view</span> (template) file:<br />The entire widget output should be contained in a div<br /><pre style="border: 1px dashed rgb(153, 153, 153); padding: 5px; overflow: auto; font-family: Andale Mono,Lucida Console,Monaco,fixed,monospace; color: rgb(0, 0, 0); background-color: rgb(238, 238, 238); font-size: 12px; line-height: 14px; width: 100%;"><code>&lt;div class="widget movable collapsable removable closeconfirm" id="widget-network_health"&gt;<br /> // widget content goes here<br />&lt;/div&gt;</code></pre>You should give the div an ID following the format: 'widget_'&lt;widget_name&gt; to make it possible to access it in a generic way using JavaScript.<br /><br />To add extra css resources you assign <code>$this->css</code> with an array like this:<br /><code>$this->css = array('/css/netw_health');</code>. The same thing is done with needed JavaScript files using <code>$this->js</code>.<br /><br />After writing the code that actually does something (left out here as I feel that it is somewhat out of scope for this post) there is only 2 more things to do:<br /><br /><code>require_once($view_path);</code><br />OK, so I stated <a target="_blank" rel="nofollow" href="http://ninja4nagios.blogspot.com/2009/04/ninja-system-description.html">earlier</a> that you didn't have to write requires. Well even though I feel that it could be optimized, I don't feel that it's such a big deal. We could probably let the widget helper class do this but hey, we aren't finished with Ninja yet ;-)<br /><br />Last thing needed is <code>return $this->fetch();</code> which will do the <code>ob_get_contents()</code> thing and pass everything back to the calling controller.<br /><br />The actual location of the widget on the rendered page, is then all up to the master controller and template - the widget should only return a view.<br /><br />The netw_health <a target="_blank" rel="nofollow" href="http://ninja.pastebin.com/ff311cf1" target="_blank">widget code</a>.<br />The <a target="_blank" rel="nofollow" href="http://ninja.pastebin.com/f45487111" target="_blank">view (template) code</a>.<br /><br />Pretty awesome, huh? :-)<div class="blogger-post-footer"><img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/9144504490896973295-497061875133126514?l=ninja4nagios.blogspot.com'/></div>