Merge branch 'maint/7.0'
[ninja.git] / application / widgets / widget_Base.php
blobe8c05b2c7493bdbb9e0a32b112b3fcf2379e7422
1 <?php defined('SYSPATH') OR die('No direct access allowed.');
3 /**
4 * widget helper class.
5 */
6 class widget_Base
8 protected $editable = TRUE; /**< An editable widget has settings that can be changed */
9 protected $movable = TRUE; /**< A movable widget can be dragged around */
10 protected $collapsable = TRUE; /**< A collapsable widget can be collapsed, so only the title bar is visible */
11 protected $removable = TRUE; /**< A removable widget can be deleted */
12 protected $closeconfirm = TRUE; /**< Whether to ask the user to confirm widget deletion */
13 protected $duplicatable = FALSE; /**< Whether the widget can be copied. Setting this to true requires testing, so default to the more backwards compatible mode */
15 public $result = false; /**< widget content result */
16 public $js = false; /**< required js resources? */
17 public $css = false; /**< additional css? */
18 public $inline_js = false; /**< additional inline javascript, as a string */
19 public $widget_base_path = false; /**< path to widget main directory */
20 public $widget_full_path = false; /**< path to this widget's directory */
21 public $model = false; /**< The widget model instance this widget represents */
22 public $extra_data_attributes = array(); /**< array Key-value to attach to widget-container (for example ["hello"] => "bye" which renders as <div data-hello="bye" />, good for javascript-hooks */
23 private static $loaded_widgets = array();
25 public $arguments = array(); /**< The arguments for this instance, constructed from the option objects */
27 /**
28 * Create new widget instance from a given widget model.
30 public function __construct($widget_model)
32 $this->widget_base_path = Kohana::config('widget.path').Kohana::config('widget.dirname');
33 $this->auto_render = FALSE;
35 $path = Kohana::find_file(Kohana::config('widget.custom_dirname').$widget_model->name, $widget_model->name, false);
36 if ($path === false) {
37 $path = Kohana::find_file(Kohana::config('widget.dirname').$widget_model->name, $widget_model->name, false);
39 if (strstr($path, Kohana::config('widget.custom_dirname')) !== false) {
40 $this->widget_base_path = Kohana::config('widget.path').Kohana::config('widget.custom_dirname');
42 $this->widget_full_path = $this->widget_base_path.$widget_model->name;
44 $this->model = $widget_model;
47 /**
48 * DEPRECATED: Do not use
50 * For legacy reasons, this provides a shortcut to a populated Current_status_Model instance.
51 * There once were significant performance advantages to use this wrapper, but there isn't anymore.
52 * Just call Current_status_Model::instance() instead.
54 public static function get_current_status() {
55 $current_status = Current_status_Model::instance();
56 $current_status->analyze_status_data();
57 return $current_status;
60 /**
61 * Returns the populated argument array
63 public function get_arguments() {
64 $arguments = array();
65 foreach ($this->options() as $option) {
66 if (!is_string($option))
67 $arguments[$option->name] = $option->value($this->model->setting);
69 if (is_array($this->model->setting)) {
70 foreach ($this->model->setting as $opt => $val) {
71 if (!isset($arguments[$opt]))
72 $arguments[$opt] = $val;
75 return $arguments;
78 /**
79 * Find path of widget viewer
80 * @param $view Template object
81 * @return str path to viewer
83 public function view_path($view=false)
86 if (empty($view))
87 return false;
89 $widget = $this->model->name;
90 # first try custom path
92 $path = Kohana::find_file(Kohana::config('widget.custom_dirname').$this->model->name, $view, false);
93 if ($path === false) {
94 # try core path if not found in custom
95 $path = Kohana::find_file(Kohana::config('widget.dirname').$this->model->name, $view, false);
98 return $path;
102 * Return the list of options to use in this widget. This should be an array of
103 * option instances, or - if you want to do more manual work - strings.
105 * Actual widgets typically want to extend this method.
107 public function options()
109 $refresh = new option($this->model->name, 'refresh_interval', 'Refresh (sec)', 'input', array('size'=>3, 'type'=>'text'), 60);
110 $refresh->should_render_js(false);
111 return array(
112 $refresh,
113 '<div class="refresh_slider"></div>'
118 * Hook to force additional CSS classes to the rendering
120 public function add_css_class ($class)
122 if (!isset($this->added_classes))
123 $this->added_classes = array();
124 $this->added_classes[] = $class;
128 * Method to render a widget
130 * @param $method Name of method
131 * @param $with_chrome True to generate widget with the menus and everything, false otherwise
132 * @return The rendered widget as a string
134 public function render($method='index', $with_chrome=true)
136 $content = '';
137 $widget_id = $this->model->name.'-'.$this->model->instance_id;
138 if ($with_chrome) {
139 $options = $this->options();
140 $widget_legal_classes = array('editable', 'movable', 'collapsable', 'removable', 'closeconfirm', 'duplicatable');
141 $widget_classes = array();
143 foreach ($widget_legal_classes as $class) {
144 if ($this->$class) {
145 $widget_classes[] = $class;
149 if (isset($this->added_classes)) {
150 for($i = 0; $i < count($this->added_classes); $i++) {
151 $widget_classes[] = $this->added_classes[$i];
154 $data_attributes = "";
155 foreach($this->extra_data_attributes as $key => $value) {
156 $data_attributes .= " data-$key='$value'";
159 $content .= '<div class="widget '.implode(' ', $widget_classes).'" id="widget-'.$widget_id.'" data-name="'.$this->model->name.'" '.$data_attributes.' data-instance_id="'.$this->model->instance_id.'">';
160 $content .= '<div class="widget-header"><span class="'.$widget_id.'_editable" id="'.$widget_id.'_title">'.$this->model->friendly_name.'</span></div>';
161 if (!empty($options) && $this->editable) {
162 $content .= '<div class="clear"></div><div class="widget-editbox">';
163 $content .= form::open('widget/save_widget_setting', array('id' => $widget_id.'_form', 'onsubmit' => 'return false;'));
164 $content .= '<fieldset>';
165 if (!isset(self::$loaded_widgets[$this->model->name]))
166 $this->inline_js .= "widget.register_widget_load('".$this->model->name."', function() {";
167 foreach ($options as $option) {
168 if (is_string($option)) {
169 $content .= $option;
171 else {
172 $content .= '<div class="widget-editbox-label"><div>'.$option->render_label($this->model->instance_id).'</div></div>';
173 $content .= '<div class="widget-editbox-field"><div>'.$option->render_widget($this->model->instance_id, $this->model->setting).'</div></div>';
174 $js = $option->render_js();
175 if (!empty($js) && !isset(self::$loaded_widgets[$this->model->name]))
176 $this->inline_js .= "($js)(this);\n";
178 $content .= '<br/>';
180 if (!isset(self::$loaded_widgets[$this->model->name]))
181 $this->inline_js .= "});\n";
182 $content .= '</fieldset>';
183 $content .= form::close();
184 $content .= '</div>';
187 $content .= '<div class="%%WIDGET_CLASS%%" style="overflow: auto;">'; // Clear and end widget header and start widget content
189 ob_start();
190 $this->$method();
192 if (gettype($this->widget_base_path) === 'boolean') {
193 $content .= '<h2>Widget Error</h2><br />';
194 $content = str_replace('%%WIDGET_CLASS%%', 'widget-content-error', $content);
195 } else {
196 $content = str_replace('%%WIDGET_CLASS%%', 'widget-content', $content);
199 $content .= ob_get_contents();
200 ob_end_clean();
201 if ($with_chrome) {
202 $content .= '</div>';
203 $content .= '</div>';
205 self::$loaded_widgets[$this->model->name] = 1;
206 return $content;
210 * Print the widget contents here
212 * Concrete widgets typically want to override this.
214 public function index()
216 echo "<p>(empty widget)</p>";
220 * Weird little method that returns all the resources referenced in this instance
222 public function resources($in_files=false, $type='js')
224 if (empty($in_files) || empty($type))
225 return array();
226 $type = strtolower($type);
227 $files = array();
228 foreach ($in_files as $file) {
229 if (file_exists($this->widget_base_path.$this->model->name.'/'.$file.'.'.$type))
230 $files[] = $this->widget_base_path.$this->model->name.'/'.$file.'.'.$type;
231 else
232 $files[] = $file.'.'.$type;
234 switch ($type) {
235 case 'css':
236 return $files;
237 break;
238 case 'js': default:
239 return $files;
240 break;
245 * Set correct paths
246 * @param $rel_path string: Relative path
247 * @return false on errors, "full relative" path on success.
249 public function add_path($rel_path)
251 $rel_path = trim($rel_path);
252 if (empty($rel_path)) {
253 return false;
256 $path = false;
257 # assume rel_path is relative to views directory
258 $path = 'application/views/'.$rel_path;
259 # make sure we didn't mix up start/end slashes
260 $path = str_replace('//', '/', $path);
261 return $path;