Completed
Push — master ( bc1ad9...b87221 )
by Askupa
01:45
created

SettingsPage::add_section()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 10
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 6
nc 2
nop 1
dl 0
loc 10
rs 9.4285
c 0
b 0
f 0
1
<?php
2
3
namespace Amarkal\Settings;
4
5
/**
6
 * Implements a settings page.
7
 */
8
class SettingsPage
9
{   
0 ignored issues
show
Coding Style introduced by
The opening class brace should be on a newline by itself.
Loading history...
10
    /**
11
     * Configuration array 
12
     * 
13
     * @var array 
14
     */
15
    private $config;
16
    
17
    /**
18
     * The UI form instance
19
     * 
20
     * @var Amarkal\UI\Form 
21
     */
22
    private $form;
23
24
    /**
25
     * The list of arguments for each section 
26
     *
27
     * @var array
28
     */
29
    private $sections;
30
31
    /**
32
     * The list of fields for the entire settings page
33
     *
34
     * @var Amarkal\UI\ComponentList
35
     */
36
    private $fields;
37
    
38
    /**
39
     * Set the config, create a form instance and add actions.
40
     * 
41
     * @param array $config
42
     */
43
    public function __construct( array $config = array() ) 
44
    {
45
        $this->config   = array_merge($this->default_args(), $config);
46
        $this->fields   = new \Amarkal\UI\ComponentList();
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Amarkal\UI\ComponentList() of type object<Amarkal\UI\ComponentList> is incompatible with the declared type object<Amarkal\Settings\Amarkal\UI\ComponentList> of property $fields.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
47
        $this->sections = array();
48
        $this->form     = new \Amarkal\UI\Form($this->fields);
0 ignored issues
show
Documentation Bug introduced by
It seems like new \Amarkal\UI\Form($this->fields) of type object<Amarkal\UI\Form> is incompatible with the declared type object<Amarkal\Settings\Amarkal\UI\Form> of property $form.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
49
        
50
        \add_action('admin_menu', array($this,'add_submenu_page'));
51
        \add_action('admin_enqueue_scripts', array($this,'enqueue_scripts'));
52
    }
53
54
    /**
55
     * Add a section to this settings page
56
     *
57
     * @param array $args
58
     * @return void
59
     */
60
    public function add_section( array $args )
61
    {
62
        $args = \array_merge($this->default_section_args(), $args);
63
        $slug = $args['slug'];
64
        if(array_key_exists($slug, $this->sections))
65
        {
66
            throw new \RuntimeException("A section with '$slug' has already been created for this page.");  
67
        }
68
        $this->sections[$slug] = $args;
69
    }
70
71
    /**
72
     * Add a settings field to this settings page
73
     *
74
     * @param array $args
75
     * @return void
76
     */
77
    public function add_field( array $args )
78
    {
79
        $this->fields->add_component($args);
80
    }
81
    
82
    /**
83
     * Internally used to add a submenu page for this settings page
84
     */
85
    public function add_submenu_page()
86
    {
87
        \add_submenu_page(
88
            $this->config['parent_slug'], 
89
            $this->config['title'], 
90
            $this->config['menu_title'], 
91
            $this->config['capability'],
92
            $this->config['slug'],
93
            array($this, 'render')
94
        );
95
    }
96
    
97
    /**
98
     * Conditionally enqueue settings scripts and styles if the calling page is
99
     * a settings page.
100
     */
101
    public function enqueue_scripts()
102
    {
103
        // Only enqueue styles & scripts if this is a settings page
104
        if($this->config['slug'] === filter_input(INPUT_GET, 'page'))
105
        {
106
            \wp_enqueue_style('amarkal-settings');
107
            \wp_enqueue_script('amarkal-settings');
108
        }
109
    }
110
    
111
    /**
112
     * Render the settings page
113
     */
114
    public function render()
115
    {
116
        $this->form->update($this->get_old_instance());
117
        include __DIR__.'/SettingsPage.phtml';
118
        \add_filter('admin_footer_text', array($this, 'footer_credits'));
119
    }
120
    
121
    /**
122
     * Ajax callback internally used to update options values for the given 
123
     * settings page.
124
     * 
125
     * @param array $new_instance
126
     * @return array
127
     */
128 View Code Duplication
    public function update( $new_instance )
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
129
    {
130
        if($this->can_update())
131
        {
132
            $old_instance = $this->get_old_instance();
133
            $final_instance = $this->form->update($new_instance, $old_instance);
134
            
135
            \update_option($this->config['slug'],$final_instance);
136
137
            return $this->results_array(
138
                $this->get_errors(),
139
                $final_instance
140
            );
141
        }
142
        return $this->results_array(
143
            array("You don't have permission to manage options on this site")
144
        );
145
    }
146
    
147
    /**
148
     * Ajax callback internally used to reset all component values to their 
149
     * defaults for the given settings page.
150
     * 
151
     * @return type
152
     */
153
    public function reset()
154
    {
155
        if($this->can_update())
156
        {
157
            \delete_option($this->config['slug']);
158
159
            return $this->results_array(
160
                array(),
161
                $this->form->reset()
162
            );
163
        }
164
        return $this->results_array(
165
            array("You don't have permission to manage options on this site")
166
        );
167
    }
168
169
    /**
170
     * Ajax callback internally used to reset all component values to their 
171
     * defaults for the given settings section.
172
     *
173
     * @param string $slug
174
     * @return void
175
     */
176 View Code Duplication
    public function reset_section( $slug ) 
0 ignored issues
show
Duplication introduced by
This method seems to be duplicated in your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
177
    {
178
        if($this->can_update())
179
        {
180
            $old_instance = $this->get_old_instance();
181
            $final_instance = $this->form->reset_components($this->get_section_fields($slug));
182
            
183
            \update_option($this->config['slug'], 
184
                // Array merge is needed in order not to delete fields from other sections
185
                // since the $final_instance only contains the fields that were reset
186
                array_merge($old_instance, $final_instance)
187
            );
188
189
            return $this->results_array(
190
                array(),
191
                $final_instance
192
            );
193
        }
194
        return $this->results_array(
195
            array("You don't have permission to manage options on this site")
196
        );
197
    }
198
    
199
    /**
200
     * Renders Amarkal's credits on the page's footer.
201
     */
202
    public function footer_credits()
203
    {
204
        echo '<span id="footer-thankyou">Created with <a href="https://github.com/askupasoftware/amarkal-settings">amarkal-settings</a>, a module within the <a href="https://github.com/askupasoftware/amarkal">Amarkal Framework</a></span>';
205
    }
206
207
    /**
208
     * Get the component corresponding to the given name
209
     *
210
     * @param [string] $name
0 ignored issues
show
Documentation introduced by
The doc-type [string] could not be parsed: Unknown type name "" at position 0. [(view supported doc-types)

This check marks PHPDoc comments that could not be parsed by our parser. To see which comment annotations we can parse, please refer to our documentation on supported doc-types.

Loading history...
211
     * @throws RuntimeException when the component cannot be found
212
     * @return void
213
     */
214
    public function get_component($name)
215
    {
216
        return $this->form->get_component_list()->get_by_name($name);
217
    }
218
219
    /**
220
     * Get all the fields for the given section
221
     *
222
     * @param string $slug
223
     * @return array
224
     */
225
    private function get_section_fields($slug)
226
    {
227
        $fields = array();
228
        foreach($this->fields->get_all() as $c)
229
        {
230
            if($c->section === $slug)
231
            {
232
                $fields[] = $c;
233
            }
234
        }
235
        return $fields;
236
    }
237
    
238
    /**
239
     * Get all errors from the form instance.
240
     * 
241
     * @return array
242
     */
243
    private function get_errors()
244
    {
245
        $errors = array();
246
        foreach($this->form->get_errors() as $name => $error)
247
        {
248
            $errors[$name] = $error;
249
        }
250
        return $errors;
251
    }
252
    
253
    /**
254
     * Generates a results array to be returned when an Ajax request is made.
255
     * 
256
     * @param array $errors The list of errors
257
     * @param array $values The list of values
258
     * @return array
259
     */
260
    private function results_array( $errors = array(), $values = '' )
261
    {
262
        return array(
263
            'values' => $values,
264
            'errors' => $errors
265
        );
266
    }
267
    
268
    /**
269
     * Check if the current user has the required privileges to update the 
270
     * settings values.
271
     * 
272
     * @return boolean
273
     */
274
    private function can_update()
275
    {
276
        return \current_user_can($this->config['capability']);
277
    }
278
    
279
    /**
280
     * Get the old instance from the database.
281
     * 
282
     * @return array
283
     */
284
    private function get_old_instance()
285
    {
286
        return \get_option($this->config['slug'], array());
287
    }
288
    
289
    /**
290
     * The default config arguments array for a page.
291
     * 
292
     * @return array
293
     */
294
    private function default_args()
295
    {
296
        return array(
297
            'parent_slug'    => '',
298
            'slug'           => '',
299
            'title'          => '',
300
            'subtitle'       => '',
301
            'menu_title'     => '',
302
            'capability'     => 'manage_options',
303
            'footer_html'    => '',
304
            'subfooter_html' => ''
305
        );
306
    }
307
308
    /**
309
     * The default config arguments array for a section.
310
     *
311
     * @return void
312
     */
313
    private function default_section_args()
314
    {
315
        return array(
316
            'slug'           => '',
317
            'title'          => '',
318
            'subtitle'       => ''
319
        );
320
    }
321
}