sla: Stupid Rasmus and his needles and haystacks
[ninja.git] / application / widgets / PORTING_GUIDE
blob749a43a27ec432f467ad45394ba328e6a1fd53f4
1 = PORTING GUIDE =
3 The widget system in ninja <= 1.2.x was somewhat different from the widget
4 system used afterwards. This document will explain how to port your widgets.
6 - JS -
7 In old-style widgets, you usually needed to create a javascript file with the
8 following content:
10 $(document).ready(function() {
11         var my_widget = new widget('my_widget', 'widget-content');
12 });
14 If that was all your javascript file contained, you can now safely remove it. If
15 it did more things, you may no longer initialize the widget yourself, but must
16 instead wait for the widget system to load your widget:
18 widget.register_widget_load('my_widget', function() {
19         var my_widget = this;
20 });
22 If your javascript also kept track of your custom configuration options, that,
23 too, can likely be removed - see EXTRA SETTINGS.
25 If you do custom things to your javascript, you may need to adjust it, should
26 you want to make it possible to create multiple instances of the widget. This is
27 described in MULTI INSTANCE WIDGETS.
29 - VIEW -
31 Old widgets that supported ajax refreshes all had to copy-paste the following:
33 <?php defined('SYSPATH') OR die('No direct access allowed.'); ?>
34 <?php if (!$ajax_call) { ?>
35 <div class="widget editable movable collapsable removable closeconfirm" id="widget-<?php echo $widget_id ?>">
36         <div class="widget-header"><span class="<?php echo $widget_id ?>_editable" id="<?php echo $widget_id ?>_title"><?php echo $title ?></span></div>
37         <div class="widget-editbox">
38                 <?php echo form::open('ajax/save_widget_setting', array('id' => $widget_id.'_form', 'onsubmit' => 'return false;')); ?>
39                 <fieldset>
40                 <label for="<?php echo $widget_id ?>_refresh"><?php echo _('Refresh (sec)') ?>:</label>
41                 <input size="3" type="text" name="<?php echo $widget_id ?>_refresh" id="<?php echo $widget_id ?>_refresh" value="<?php echo $refresh_rate ?>" />
42                 <div id="<?php echo $widget_id ?>_slider"></div>
43 <!-- EXTRA CONTROLS HERE -->
44                 </fieldset>
45                 <?php echo form::close() ?>
46         </div>
47         <div class="widget-content">
48 <?php } ?>
49 <!-- WIDGET CONTENT HERE -->
50 <?php if (!$ajax_call) { ?>
51         </div>
52 </div>
53 <?php } ?>
55 With the new widget system, you should remove everything from this file that
56 isn't content. That is, the only thing you should keep in the view, is what you
57 had where it says <!-- WIDGET CONTENT HERE --> - everything else should go. Any
58 extra controls will need to be migrated to the controller - see EXTRA SETTINGS
59 below.
61 - CONTROLLER -
63 This is the old template for the controller, i.e. the file that had the name of
64 your widget. Not all widgets had all of this, but most of them had most of it:
66 <?php defined('SYSPATH') OR die('No direct access allowed.');
67 class My_widget_Widget extends widget_Core {
68         public function __construct()
69         {
70                 parent::__construct();
72                 # needed to figure out path to widget
73                 $this->set_widget_name(__CLASS__, basename(__FILE__));
74         }
76         public function index($arguments=false, $master=false)
77         {
78                 # required to enable us to assign the correct
79                 # variables to the calling controller
80                 $this->master_obj = $master;
82                 # fetch widget view path
83                 $view_path = $this->view_path('view');
85                 if (is_object($arguments[0])) {
86                         $current_status = $arguments[0];
87                         array_shift($arguments);
88                 } else {
89                         $current_status = new Current_status_Model();
90                 }
92                 if (!$current_status->data_present()) {
93                         $current_status->analyze_status_data();
94                 }
96                 $widget_id = $this->widgetname;
97                 if (isset($arguments['refresh_interval'])) {
98                         $refresh_rate = $arguments['refresh_interval'];
99                 }
101                 $title = _('My Widget');
102                 if (isset($arguments['widget_title'])) {
103                         $title = $arguments['widget_title'];
104                 }
106                 # let view template know if wrapping div should be hidden or not
107                 $ajax_call = request::is_ajax() ? true : false;
109                 /**
110                  * Actually do stuff
111                  */
113                 # fetch widget content
114                 require_once($view_path);
116                 if(request::is_ajax()) {
117                         # output widget content
118                         echo json::encode( $this->output());
119                 } else {
120                         $this->js = array('/js/my_widget');
121                         $this->css = array('/css/my_widget');
122                         # call parent helper to assign all
123                         # variables to master controller
124                         return $this->fetch();
125                 }
126         }
129 This is the new-style equivalent:
131 <?php defined('SYSPATH') OR die('No direct access allowed.');
132 class My_widget_Widget extends widget_Base {
133         public function __construct($model)
134         {
135                 parent::__construct($model);
136                 /**
137                  * Do any global initiation here
138                  */
139         }
141         public function index()
142         {
143                 # fetch widget view path
144                 $view_path = $this->view_path('view');
146                 $current_status = $this->get_current_status();
147                 $arguments = $this->get_arguments();
149                 /**
150                  * Actually do stuff
151                  */
153                 $this->js = array('/js/my_widget');
154                 $this->css = array('/css/my_widget');
155                 require($view_path);
156         }
159 Note:
160  * The widget must inherit from widget_Base instead of widget_Core.
161  * The constructor now takes an argument, index takes none.
162  * You must not use require_once to include the view if you intend to allow
163    multiple widget instances.
164  * Still no file endings on javascript and css resources.
166 - EXTRA SETTINGS -
167 This used to be a free-form div in the view, however, it was mostly just
168 cut'n'pasted from widget to widget, so we have implemented the redundant stuff
169 once, so you won't have to. You now add a method to the controller, options, and
170 have it return an array of your extra options. This is the last example widget
171 again, but with two extra settings:
173 <?php defined('SYSPATH') OR die('No direct access allowed.');
174 class My_widget_Widget extends widget_Base {
175         public function options() {
176                 $options = parent::options();
177                 $options[] = new option('my_widget', // your widget name (or something else unique)
178                                         'option1', // a unique option name
179                                         _('My first option'), // your label
180                                         'input', // option type - input, checkbox, dropdown, etc
181                                         array('size'=>5), // extra attributes for the field
182                                         'default1'); // default value
183                 $options[] = new option('my_widget',
184                                         'option2',
185                                         'My second option',
186                                         'input',
187                                         array('size'=>5),
188                                         'default2');
189                 return $options;
190         }
192         public function index()
193         {
194                 # fetch widget view path
195                 $view_path = $this->view_path('view');
197                 $current_status = $this->get_current_status();
198                 $arguments = $this->get_arguments();
200                 /**
201                  * Actually do stuff
202                  */
204                 require($view_path);
205         }
208 NOTE:
209  * This will automatically create javascript to save any changes and refresh the
210    page on changes. If you want to do this manually, you must call
211    should_render_js(false) on the option object.
212  * If you want to, you can return a pure HTML string of the widget settings you
213    want to keep track of. That way, you will get to do everything yourself.
215 - MULTI INSTANCE WIDGETS -
216 For simple widgets, to enable multiple instances you will only have to add one
217 single line of code to the constructor: "protected $duplicatable = true;". This
218 is a simple hello world widget that can be duplicated:
220 <?php defined('SYSPATH') OR die('No direct access allowed.');
221 class My_widget_Widget extends widget_Base {
222         protected $duplicatable = true;
223         public function index()
224         {
225                 print "Hello world!";
226         }
229 If your widget is more complicated, you will probably have to change more
230 things.
232 First, it's likely that your widget includes fields with the id attribute. Doing
233 so is no longer valid - you must either create a globally unique name using both
234 the widget's name and instance id, or you should use a class attribute instead.
236 Then, in your javascript, you must take care to use a combined selector to
237 retrieve the HTML node you want to for the correct widget instance. In the past,
238 a common pattern in javascript files was the following:
240 $(document).ready(function() {
241         var my_widget = new widget('my_widget', 'widget-content');
242         $('#my_widget_setting').change(function() {
243                 my_widget.save_custom_val($(this).val(), 'my_widget_setting');
244                 do_something();
245         });
248 Again, if do_something is only a widget reload, you can remove this code
249 completely. If you do more things, you need to take more care.
251 This is how the above should be written with new-style, multi-instance widgets:
253 widget.register_widget_load('my_widget', function() {
254         var my_widget = this;
255         $('#'+my_widget.widget_id+' .my_widget_setting').change(function() {
256                 my_widget.save_custom_val($(this).val(), 'my_widget_setting');
257                 do_something();
258         });
261 That is, you can safely search for the class within the widget instance id.