1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
namespace Carbon_Fields\Widget; |
4
|
|
|
|
5
|
|
|
use Carbon_Fields\Helper\Helper; |
6
|
|
|
use Carbon_Fields\Field\Field; |
7
|
|
|
use Carbon_Fields\Container\Container; |
8
|
|
|
use Carbon_Fields\Datastore\Datastore; |
9
|
|
|
use Carbon_Fields\Exception\Incorrect_Syntax_Exception; |
10
|
|
|
|
11
|
|
|
/** |
12
|
|
|
* Widget, datastore and container handler class. |
13
|
|
|
*/ |
14
|
|
|
abstract class Widget extends \WP_Widget { |
15
|
|
|
public static $registered_widget_ids = array(); |
16
|
|
|
|
17
|
|
|
/** |
18
|
|
|
* Widget Datastore |
19
|
|
|
* |
20
|
|
|
* @var Widget_Datastore |
21
|
|
|
*/ |
22
|
|
|
protected $datastore; |
23
|
|
|
|
24
|
|
|
/** |
25
|
|
|
* Determines if widget wrapper html should be printed |
26
|
|
|
* |
27
|
|
|
* @see widget() |
28
|
|
|
* @var bool |
29
|
|
|
*/ |
30
|
|
|
protected $print_wrappers = true; |
31
|
|
|
|
32
|
|
|
/** |
33
|
|
|
* Control options to pass to WordPress Widget constructor |
34
|
|
|
* |
35
|
|
|
* @see setup() |
36
|
|
|
* @var array |
37
|
|
|
*/ |
38
|
|
|
protected $widget_control_options = array( 'width' => 295 ); |
39
|
|
|
|
40
|
|
|
/** |
41
|
|
|
* Array of Carbon Fields for the widget |
42
|
|
|
* |
43
|
|
|
* @var array |
44
|
|
|
*/ |
45
|
|
|
protected $custom_fields = array(); |
46
|
|
|
|
47
|
|
|
/** |
48
|
|
|
* Create the widget. |
49
|
|
|
* A wrapper around the default WP widget constructor. |
50
|
|
|
* |
51
|
|
|
* @param string $title Widget name |
52
|
|
|
* @param string $description Widget description |
53
|
|
|
* @param array $fields Array of fields |
54
|
|
|
* @param string $classname String of CSS classes |
55
|
|
|
*/ |
56
|
|
|
public function setup( $title, $description, $fields, $classname = '' ) { |
57
|
|
|
\Carbon_Fields\Carbon_Fields::verify_boot(); |
58
|
|
|
|
59
|
|
|
$this->datastore = Datastore::make( 'widget' ); |
|
|
|
|
60
|
|
|
if ( empty( $title ) ) { |
61
|
|
|
Incorrect_Syntax_Exception::raise( 'Empty widget title is not supported' ); |
62
|
|
|
} |
63
|
|
|
|
64
|
|
|
$this->add_fields( $fields ); |
65
|
|
|
|
66
|
|
|
# Generate Widget ID |
67
|
|
|
$widget_id = 'carbon_widget_' . preg_replace( '~\s+~', '_', strtolower( trim( preg_replace( '/[^a-zA-Z0-9]+/u', '', remove_accents( $title ) ) ) ) ); |
68
|
|
|
|
69
|
|
|
$this->register_widget_id( $widget_id ); |
70
|
|
|
|
71
|
|
|
# Generate Classes |
72
|
|
|
if ( ! is_array( $classname ) ) { |
73
|
|
|
$classname = (array) $classname; |
74
|
|
|
} |
75
|
|
|
$classname[] = $widget_id; |
76
|
|
|
$classname = array_filter( $classname ); |
77
|
|
|
$classname = implode( ' ', $classname ); |
78
|
|
|
|
79
|
|
|
$widget_options = array( |
80
|
|
|
'description' => $description, |
81
|
|
|
'classname' => $classname, |
82
|
|
|
'widget_ID' => $widget_id, |
83
|
|
|
); |
84
|
|
|
|
85
|
|
|
parent::__construct( $widget_id, $title, $widget_options, $this->widget_control_options ); |
86
|
|
|
} |
87
|
|
|
|
88
|
|
|
/** |
89
|
|
|
* Updates a particular instance of a widget. |
90
|
|
|
* |
91
|
|
|
* @param array $new_instance New settings for this instance as input by the user via |
92
|
|
|
* WP_Widget::form(). |
93
|
|
|
* @param array $old_instance Old settings for this instance. |
94
|
|
|
* @return array Settings to save or bool false to cancel saving. |
95
|
|
|
*/ |
96
|
|
|
public function update( $new_instance, $old_instance ) { |
97
|
|
|
$this->datastore->import_storage( $old_instance ); |
98
|
|
|
|
99
|
|
|
foreach ( $this->custom_fields as $field ) { |
100
|
|
|
$field->set_value_from_input( $new_instance ); |
101
|
|
|
$field->save(); |
102
|
|
|
} |
103
|
|
|
|
104
|
|
|
return $this->datastore->export_storage(); |
105
|
|
|
} |
106
|
|
|
|
107
|
|
|
/** |
108
|
|
|
* Outputs the settings update form. |
109
|
|
|
* |
110
|
|
|
* @param array $instance Current settings. |
111
|
|
|
*/ |
112
|
|
|
public function form( $instance ) { |
113
|
|
|
$this->datastore->import_storage( $instance ); |
114
|
|
|
$custom_fields = array(); |
115
|
|
|
|
116
|
|
|
foreach ( $this->custom_fields as $field ) { |
117
|
|
|
$tmp_field = clone $field; |
118
|
|
|
$tmp_field->load(); |
119
|
|
|
|
120
|
|
|
$field_name = $this->get_field_name( $tmp_field->get_name() ); |
121
|
|
|
$tmp_field->set_name( $field_name ); |
122
|
|
|
|
123
|
|
|
$custom_fields[] = $tmp_field; |
124
|
|
|
} |
125
|
|
|
|
126
|
|
|
Container::factory( 'widget', $this->id ) |
127
|
|
|
->add_fields( $custom_fields ) |
128
|
|
|
->init(); |
129
|
|
|
} |
130
|
|
|
|
131
|
|
|
/** |
132
|
|
|
* Echoes the widget content. |
133
|
|
|
* Sub-classes can over-ride this method to generate their widget code |
134
|
|
|
* but it is best to override front_end(). |
135
|
|
|
* |
136
|
|
|
* @param array $args Display arguments including 'before_title', 'after_title', |
137
|
|
|
* 'before_widget', and 'after_widget'. |
138
|
|
|
* @param array $instance The settings for the particular instance of the widget. |
139
|
|
|
*/ |
140
|
|
|
public function widget( $args, $instance ) { |
141
|
|
|
if ( $this->print_wrappers ) { |
142
|
|
|
echo $args['before_widget']; |
143
|
|
|
} |
144
|
|
|
|
145
|
|
|
$this->front_end( $args, $instance ); |
146
|
|
|
|
147
|
|
|
if ( $this->print_wrappers ) { |
148
|
|
|
echo $args['after_widget']; |
149
|
|
|
} |
150
|
|
|
} |
151
|
|
|
|
152
|
|
|
/** |
153
|
|
|
* The actual content of the widget. |
154
|
|
|
* Generally should be overriden by the specific widget classes. |
155
|
|
|
* @param array $args Display arguments including 'before_title', 'after_title', |
156
|
|
|
* 'before_widget', and 'after_widget'. |
157
|
|
|
* @param array $instance The settings for the particular instance of the widget. |
158
|
|
|
*/ |
159
|
|
|
public function front_end( $args, $instance ) { } |
160
|
|
|
|
161
|
|
|
/** |
162
|
|
|
* Append array of fields to the current fields set. All items of the array |
163
|
|
|
* must be instances of Field and their names should be unique for all |
164
|
|
|
* Carbon containers. |
165
|
|
|
* |
166
|
|
|
* @param array $fields |
167
|
|
|
**/ |
168
|
|
|
public function add_fields( $fields ) { |
169
|
|
|
foreach ( $fields as $field ) { |
170
|
|
|
if ( ! is_a( $field, 'Carbon_Fields\\Field\\Field' ) ) { |
171
|
|
|
Incorrect_Syntax_Exception::raise( 'Object must be of type Carbon_Fields\\Field\\Field' ); |
172
|
|
|
return; |
173
|
|
|
} |
174
|
|
|
$this->register_field_name( $field->get_name() ); |
175
|
|
|
$field->set_name_prefix( '' ); |
176
|
|
|
$field->set_datastore( $this->datastore, true ); |
177
|
|
|
} |
178
|
|
|
$this->custom_fields = array_merge( $this->custom_fields, $fields ); |
179
|
|
|
} |
180
|
|
|
|
181
|
|
|
/** |
182
|
|
|
* Verify widget field names are unique. |
183
|
|
|
* |
184
|
|
|
* @param string $name Field name |
185
|
|
|
* @return boolean |
186
|
|
|
*/ |
187
|
|
|
public function register_field_name( $name ) { |
188
|
|
|
static $registered_field_names = array(); |
189
|
|
|
|
190
|
|
|
if ( in_array( $name, $registered_field_names ) ) { |
191
|
|
|
Incorrect_Syntax_Exception::raise( 'Field name "' . $name . '" already registered' ); |
192
|
|
|
return false; |
193
|
|
|
} |
194
|
|
|
|
195
|
|
|
$registered_field_names[] = $name; |
196
|
|
|
return true; |
197
|
|
|
} |
198
|
|
|
|
199
|
|
|
/** |
200
|
|
|
* Verify widget IDs are unique. |
201
|
|
|
* |
202
|
|
|
* @param string $id Widget ID |
203
|
|
|
*/ |
204
|
|
|
public function register_widget_id( $id ) { |
205
|
|
|
if ( in_array( $id, static::$registered_widget_ids ) ) { |
206
|
|
|
Incorrect_Syntax_Exception::raise( 'Widget with ID "' . $id . '" already registered. Please change the widget title' ); |
207
|
|
|
return; |
208
|
|
|
} |
209
|
|
|
|
210
|
|
|
static::$registered_widget_ids[] = $id; |
211
|
|
|
} |
212
|
|
|
} |
213
|
|
|
|
Our type inference engine has found a suspicous assignment of a value to a property. This check raises an issue when a value that can be of a mixed type is assigned to a property that is type hinted more strictly.
For example, imagine you have a variable
$accountId
that can either hold an Id object or false (if there is no account id yet). Your code now assigns that value to theid
property of an instance of theAccount
class. This class holds a proper account, so the id value must no longer be false.Either this assignment is in error or a type check should be added for that assignment.