Form   F
last analyzed

Complexity

Total Complexity 89

Size/Duplication

Total Lines 534
Duplicated Lines 0 %

Coupling/Cohesion

Components 2
Dependencies 6

Importance

Changes 0
Metric Value
dl 0
loc 534
rs 2
c 0
b 0
f 0
wmc 89
lcom 2
cbo 6

34 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 26 5
C isValid() 0 67 13
A recover() 0 8 2
A clearValues() 0 6 2
A clearErrors() 0 6 2
A setSessionValue() 0 4 1
A setError() 0 14 4
A getFormId() 0 4 1
A renderAjaxErrorResponse() 0 7 2
A setSuccess() 0 4 1
A __sleep() 0 4 1
A addElement() 0 14 3
A getAjax() 0 4 1
A getElements() 0 4 1
A getError() 0 4 1
A getId() 0 4 1
A getJQueryUIButtons() 0 4 1
A getPrevent() 0 4 1
A getResourcesPath() 0 4 1
A getErrors() 0 15 3
A getWidth() 0 4 1
A getWidthSuffix() 0 4 1
A render() 0 31 4
A getSessionValues() 0 7 2
A setValues() 0 4 1
A applyValues() 0 13 5
A formatWidthProperties() 0 15 4
A renderCSS() 0 10 2
B renderJS() 0 81 8
A renderJSFiles() 0 27 5
A save() 0 4 1
A renderCSSFiles() 0 24 5
A isNotJQueryUiButtons() 0 4 1
A isFormFocusNotOnHomepage() 0 5 2

How to fix   Complexity   

Complex Class

Complex classes like Form often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes. You can also have a look at the cohesion graph to spot any un-connected, or weakly-connected components.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Form, and based on these observations, apply Extract Interface, too.

1
<?php
0 ignored issues
show
Coding Style Compatibility introduced by
For compatibility and reusability of your code, PSR1 recommends that a file should introduce either new symbols (like classes, functions, etc.) or have side-effects (like outputting something, or including other files), but not both at the same time. The first symbol is defined on line 12 and the first side effect is on line 20.

The PSR-1: Basic Coding Standard recommends that a file should either introduce new symbols, that is classes, functions, constants or similar, or have side effects. Side effects are anything that executes logic, like for example printing output, changing ini settings or writing to a file.

The idea behind this recommendation is that merely auto-loading a class should not change the state of an application. It also promotes a cleaner style of programming and makes your code less prone to errors, because the logic is not spread out all over the place.

To learn more about the PSR-1, please see the PHP-FIG site on the PSR-1.

Loading history...
2
/**
3
 *  This file has been modified by pH7 developers team (Pierre-Henry SORIA).
4
 */
5
6
namespace PFBC;
7
8
use PH7\Framework\Layout\Html\Design;
9
use PH7\Framework\Mvc\Request\Http as HttpRequest;
10
11
/*This project's namespace structure is leveraged to autoload requested classes at runtime.*/
12
function load($class)
13
{
14
    $file = __DIR__ . '/../' . str_replace('\\', PH7_DS, $class) . '.php';
15
    if (is_file($file)) {
16
        include $file;
17
    }
18
}
19
20
spl_autoload_register('PFBC\load');
21
if (in_array('__autoload', spl_autoload_functions(), true)) {
22
    spl_autoload_register('__autoload');
23
}
24
25
class Form extends Base
26
{
27
    private static $sFormId;
28
    protected $ajax;
29
    protected $attributes;
30
    protected $error;
31
    protected $jQueryUITheme = 'smoothness';
32
    protected $resourcesPath;
33
    protected $prevent = [];
34
    protected $view;
35
    /*jQueryUI themes can be previewed at http://jqueryui.com/themeroller/.*/
36
    protected $width;
37
    private $elements = [];
38
    /*Prevents various automated from being automatically applied.  Current options for this array
39
    included jQuery, jQueryUI, jQueryUIButtons, focus, and style.*/
40
    private $prefix = 'http';
41
    private $values = [];
42
    private $ajaxCallback;
43
    private $widthSuffix = 'px';
44
45
    public function __construct($id = 'pfbc', $width = '')
0 ignored issues
show
Coding Style introduced by
__construct uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
46
    {
47
        self::$sFormId = $id;
48
        $this->configure([
49
            'width' => $width,
50
            'action' => basename($_SERVER['SCRIPT_NAME']),
51
            'id' => preg_replace("/\W/", '-', $id),
52
            'method' => 'post'
53
        ]);
54
55
        if (isset($_SERVER['HTTPS']) && $_SERVER['HTTPS'] == 'on') {
56
            $this->prefix = 'https';
57
        }
58
59
        /*The Standard view class is applied by default and will be used unless a different view is
60
        specified in the form's configure method*/
61
        if (empty($this->view)) {
62
            $this->view = new View\CStandard;
63
        }
64
65
        if (empty($this->error)) {
66
            $this->error = new Error\Standard;
67
        }
68
69
        $this->resourcesPath = PH7_URL_STATIC . 'PFBC';
70
    }
71
72
    public static function isValid($id = 'pfbc', $clearValues = true)
0 ignored issues
show
Coding Style introduced by
isValid uses the super-global variable $_SERVER which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
isValid uses the super-global variable $_POST which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
isValid uses the super-global variable $_GET which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
Coding Style introduced by
isValid uses the super-global variable $_FILES which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
73
    {
74
        $valid = true;
75
        /*The form's instance is recovered (unserialized) from the session.*/
76
        $form = self::recover($id);
77
        if (!empty($form)) {
78
            if ($_SERVER['REQUEST_METHOD'] == 'POST')
79
                $data = $_POST;
80
            else
81
                $data = $_GET;
82
83
            /*Any values/errors stored in the session for this form are cleared.*/
84
            self::clearValues($id);
85
            self::clearErrors($id);
86
87
            /*Each element's value is saved in the session and checked against any validation rules applied
88
            to the element.*/
89
            if (!empty($form->elements)) {
90
                foreach ($form->elements as $element) {
91
                    $name = $element->getName();
92
                    if (substr($name, -2) == '[]') {
93
                        $name = substr($name, 0, -2);
94
                    }
95
96
                    /*The File element must be handled differently b/c it uses the $_FILES superglobal and
97
                    not $_GET or $_POST.*/
98
                    if ($element instanceof Element\File) {
99
                        $data[$name] = $_FILES[$name]['name'];
100
                    }
101
102
                    if (isset($data[$name])) {
103
                        $value = $data[$name];
104
                        if (is_array($value)) {
105
                            $valueSize = sizeof($value);
106
                            for ($v = 0; $v < $valueSize; ++$v) {
107
                                $value[$v] = stripslashes($value[$v]);
108
                            }
109
                        } else {
110
                            $value = stripslashes($value);
111
                        }
112
                        self::setSessionValue($id, $name, $value);
113
                    } else {
114
                        $value = null;
115
                    }
116
117
                    /*If a validation error is found, the error message is saved in the session along with
118
                    the element's name.*/
119
                    if (!$element->isValid($value)) {
120
                        self::setError($id, $element->getErrors(), $name);
121
                        $valid = false;
122
                    }
123
                }
124
            }
125
126
            /*If no validation errors were found, the form's session values are cleared.*/
127
            if ($valid) {
128
                if ($clearValues) {
129
                    self::clearValues($id);
130
                }
131
                self::clearErrors($id);
132
            }
133
        } else {
134
            $valid = false;
135
        }
136
137
        return $valid;
138
    }
139
140
    private static function recover($id)
0 ignored issues
show
Coding Style introduced by
recover uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
141
    {
142
        if (!empty($_SESSION['pfbc'][$id]['form'])) {
143
            return unserialize($_SESSION['pfbc'][$id]['form']);
144
        }
145
146
        return '';
147
    }
148
149
    public static function clearValues($id = 'pfbc')
0 ignored issues
show
Coding Style introduced by
clearValues uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
150
    {
151
        if (!empty($_SESSION['pfbc'][$id]['values'])) {
152
            unset($_SESSION['pfbc'][$id]['values']);
153
        }
154
    }
155
156
    public static function clearErrors($id = 'pfbc')
0 ignored issues
show
Coding Style introduced by
clearErrors uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
157
    {
158
        if (!empty($_SESSION['pfbc'][$id]['errors'])) {
159
            unset($_SESSION['pfbc'][$id]['errors']);
160
        }
161
    }
162
163
    public static function setSessionValue($id, $element, $value)
0 ignored issues
show
Coding Style introduced by
setSessionValue uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
164
    {
165
        $_SESSION['pfbc'][$id]['values'][$element] = $value;
166
    }
167
168
    /**
169
     * Validation errors are saved in the session after the form submission, and will be displayed to the user
170
     * when redirected back to the form.
171
     */
172
    public static function setError($id, $messages, $element = '')
0 ignored issues
show
Coding Style introduced by
setError uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
173
    {
174
        if (!is_array($messages)) {
175
            $messages = [$messages];
176
        }
177
178
        if (empty($_SESSION['pfbc'][$id]['errors'][$element])) {
179
            $_SESSION['pfbc'][$id]['errors'][$element] = [];
180
        }
181
182
        foreach ($messages as $message) {
183
            $_SESSION['pfbc'][$id]['errors'][$element][] = $message;
184
        }
185
    }
186
187
    /**
188
     * @return string The ID of the form.
189
     */
190
    public static function getFormId()
191
    {
192
        return self::$sFormId;
193
    }
194
195
    /**
196
     * When ajax is used to submit the form's data, validation errors need to be manually sent back to the
197
     * form using json.
198
     *
199
     * @param string $id
200
     */
201
    public static function renderAjaxErrorResponse($id = 'pfbc')
202
    {
203
        $form = self::recover($id);
204
        if (!empty($form)) {
205
            $form->error->renderAjaxErrorResponse();
206
        }
207
    }
208
209
    public static function setSuccess($id, $message, $element = '')
0 ignored issues
show
Unused Code introduced by
The parameter $id is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
Unused Code introduced by
The parameter $element is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
210
    {
211
        return (new Design)->setFlashMsg($message, Design::SUCCESS_TYPE);
212
    }
213
214
    /**
215
     * When a form is serialized and stored in the session, this function prevents any
216
     * non-essential information from being included.
217
     */
218
    public function __sleep()
219
    {
220
        return ['attributes', 'elements', 'error'];
221
    }
222
223
    public function addElement(Element $element)
224
    {
225
        $element->setForm($this);
226
        //If the element doesn't have a specified id, a generic identifier is applied.
227
        $id = $element->getID();
228
        if (empty($id))
229
            $element->setID($this->attributes['id'] . '-element-' . sizeof($this->elements));
230
        $this->elements[] = $element;
231
232
        /*For ease-of-use, the form tag's encytype attribute is automatically set if the File element
233
        class is added.*/
234
        if ($element instanceof Element\File)
235
            $this->attributes['enctype'] = 'multipart/form-data';
236
    }
237
238
    public function getAjax()
239
    {
240
        return $this->ajax;
241
    }
242
243
    public function getElements()
244
    {
245
        return $this->elements;
246
    }
247
248
    public function getError()
249
    {
250
        return $this->error;
251
    }
252
253
    public function getId()
254
    {
255
        return $this->attributes['id'];
256
    }
257
258
    public function getJQueryUIButtons()
259
    {
260
        return $this->jQueryUIButtons;
261
    }
262
263
    public function getPrevent()
264
    {
265
        return $this->prevent;
266
    }
267
268
    public function getResourcesPath()
269
    {
270
        return $this->resourcesPath;
271
    }
272
273
    public function getErrors()
0 ignored issues
show
Coding Style introduced by
getErrors uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
274
    {
275
        $errors = [];
276
        if (session_status() !== PHP_SESSION_ACTIVE) {
277
            $errors[''] = ['Error: pH7CMS requires an active session to work properly.  Simply add session_start() to your script before any output has been sent to the browser.'];
278
        } else {
279
            $errors = [];
280
            $id = $this->attributes['id'];
281
            if (!empty($_SESSION['pfbc'][$id]['errors'])) {
282
                $errors = $_SESSION['pfbc'][$id]['errors'];
283
            }
284
        }
285
286
        return $errors;
287
    }
288
289
    public function getWidth()
290
    {
291
        return $this->width;
292
    }
293
294
    public function getWidthSuffix()
295
    {
296
        return $this->widthSuffix;
297
    }
298
299
    public function render($returnHTML = false)
300
    {
301
        $this->view->setForm($this);
302
        $this->error->setForm($this);
303
304
        /*When validation errors occur, the form's submitted values are saved in a session
305
        array, which allows them to be pre-populated when the user is redirected to the form.*/
306
        $values = self::getSessionValues($this->attributes['id']);
307
        if (!empty($values))
308
            $this->setValues($values);
309
        $this->applyValues();
310
311
        $this->formatWidthProperties();
312
313
        if ($returnHTML) {
314
            ob_start();
315
        }
316
317
        $this->renderCSS();
318
        $this->view->render();
319
        $this->renderJS();
320
321
        /*The form's instance is serialized and saved in a session variable for use during validation.*/
322
        $this->save();
323
324
        if ($returnHTML) {
325
            $html = ob_get_contents();
326
            ob_end_clean();
327
            return $html;
328
        }
329
    }
330
331
    public static function getSessionValues($id = 'pfbc')
0 ignored issues
show
Coding Style introduced by
getSessionValues uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
332
    {
333
        $values = [];
334
        if (!empty($_SESSION['pfbc'][$id]['values']))
335
            $values = $_SESSION['pfbc'][$id]['values'];
336
        return $values;
337
    }
338
339
    /**
340
     * An associative array is used to pre-populate form elements.  The keys of this array correspond with
341
     * the element names.
342
     *
343
     * @param array $values
344
     */
345
    public function setValues(array $values)
346
    {
347
        $this->values = array_merge($this->values, $values);
348
    }
349
350
    /**
351
     * Values that have been set through the setValues method, either manually by the developer
352
     * or after validation errors, are applied to elements within this method.
353
     */
354
    private function applyValues()
355
    {
356
        foreach ($this->elements as $element) {
357
            $name = $element->getName();
358
            if (isset($this->values[$name])) {
359
                $element->setValue($this->values[$name]);
360
            } elseif (substr($name, -2) == '[]' &&
361
                isset($this->values[substr($name, 0, -2)])
362
            ) {
363
                $element->setValue($this->values[substr($name, 0, -2)]);
364
            }
365
        }
366
    }
367
368
    /**
369
     * This method parses the form's width property into a numeric width value and a width suffix - either px or %.
370
     * These values are used by the form's concrete view class.
371
     */
372
    public function formatWidthProperties()
373
    {
374
        if (!empty($this->width)) {
375
            if (substr($this->width, -1) == '%') {
376
                $this->width = substr($this->width, 0, -1);
377
                $this->widthSuffix = '%';
378
            } elseif (substr($this->width, -2) == 'px') {
379
                $this->width = substr($this->width, 0, -2);
380
            }
381
        } else {
382
            /*If the form's width property is empty, 100% will be assumed.*/
383
            $this->width = 100;
384
            $this->widthSuffix = '%';
385
        }
386
    }
387
388
    private function renderCSS()
389
    {
390
        echo '<style scoped="scoped">';
391
        $this->view->renderCSS();
392
        $this->error->renderCSS();
393
        foreach ($this->elements as $element) {
394
            $element->renderCSS();
395
        }
396
        echo '</style>';
397
    }
398
399
    private function renderJS()
400
    {
401
        $this->renderJSFiles();
402
        echo '<script>';
403
        $this->view->renderJS();
404
        foreach ($this->elements as $element) {
405
            $element->renderJS();
406
        }
407
408
        $id = $this->attributes['id'];
409
410
        echo 'jQuery(document).ready(function() {';
411
412
        /*When the form is submitted, disable all submit buttons to prevent duplicate submissions.*/
413
        echo 'jQuery("#', $id, '").bind("submit", function() {';
414
        if ($this->isNotJQueryUiButtons()) {
415
            echo 'jQuery(this).find("button[type=submit]").button("disable");';
416
            echo 'jQuery(this).find("button[type=submit] span.ui-button-text").css("padding-right", "2.1em").append(\'<img class="pfbc-loading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAIiIiAAAAGdnZyMjIwAAADQ0NEVFRU5OTiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" alt="Loading..."/>\');';
417
        } else {
418
            echo 'jQuery(this).find("button[type=submit]").attr("disabled", "disabled");';
419
        }
420
        echo '});';
421
422
        // Don't want to focus the first form field on the homepage
423
        if ($this->isFormFocusNotOnHomepage()) {
424
            // Use jQuery to set the focus of the form's initial element
425
            echo 'jQuery("#', $id, ' :input:visible:enabled:first").focus();';
426
        }
427
428
        $this->view->jQueryDocumentReady();
429
        foreach ($this->elements as $element) {
430
            $element->jQueryDocumentReady();
431
        }
432
433
        /*For ajax, an anonymous onsubmit javascript function is bound to the form using jQuery.  jQuery's
434
        serialize function is used to grab each element's name/value pair.*/
435
        if (!empty($this->ajax)) {
436
            echo 'jQuery("#', $id, '").bind("submit", {';
437
            $this->error->clear();
438
            echo <<<JS
439
            jQuery.ajax({
440
                url: "{$this->attributes["action"]}",
441
                type: "{$this->attributes["method"]}",
442
                data: jQuery("#$id").serialize(),
443
                success: function(response) {
444
                    if(typeof response != "undefined" && typeof response == "object" && response.errors) {
445
JS;
446
            $this->error->applyAjaxErrorResponse();
447
            echo <<<JS
448
                        jQuery("html, body").animate({ scrollTop: jQuery("#$id").offset().top }, 500 );
449
                    }
450
                    else {
451
JS;
452
            /*A callback function can be specified to handle any post submission events.*/
453
            if (!empty($this->ajaxCallback)) {
454
                echo $this->ajaxCallback, '(response);';
455
            }
456
457
            echo '}';
458
459
            if ($this->isNotJQueryUiButtons()) {
460
                echo 'jQuery("#', $id, ' button[type=submit] span.ui-button-text").css("padding-right", "1em").find("img").remove();';
461
                echo 'jQuery("#', $id, ' button[type=submit]").button("enable");';
462
            } else {
463
                echo 'jQuery("#', $id, '").find("button[type=submit]").removeAttr("disabled");';
464
            }
465
466
            echo <<<JS
467
                }
468
            });
469
            return false;
470
        });
471
472
JS;
473
        }
474
475
        echo <<<JS
476
    });
477
</script>
478
JS;
479
    }
480
481
    private function renderJSFiles()
482
    {
483
        $urls = [];
484
485
        /**
486
         * These files are already included by default in layout.tpl, therefore it is unnecessary to include them again.
487
         *
488
         * if(!in_array("jQuery", $this->prevent))
489
         * $urls[] = $this->_prefix . "://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js";
490
         * if(!in_array("jQueryUI", $this->prevent))
491
         * $urls[] = $this->_prefix . "://ajax.googleapis.com/ajax/libs/jqueryui/1/jquery-ui.min.js";
492
         */
493
        foreach ($this->elements as $element) {
494
            $elementUrls = $element->getJSFiles();
495
            if (is_array($elementUrls)) {
496
                $urls = array_merge($urls, $elementUrls);
497
            }
498
        }
499
500
        /*This section prevents duplicate css files from being loaded.*/
501
        if (!empty($urls)) {
502
            $urls = array_values(array_unique($urls));
503
            foreach ($urls as $url) {
504
                echo '<script src="', $url, '"></script>';
505
            }
506
        }
507
    }
508
509
    /**
510
     * The save method serialized the form's instance and saves it in the session.
511
     */
512
    private function save()
0 ignored issues
show
Coding Style introduced by
save uses the super-global variable $_SESSION which is generally not recommended.

Instead of super-globals, we recommend to explicitly inject the dependencies of your class. This makes your code less dependent on global state and it becomes generally more testable:

// Bad
class Router
{
    public function generate($path)
    {
        return $_SERVER['HOST'].$path;
    }
}

// Better
class Router
{
    private $host;

    public function __construct($host)
    {
        $this->host = $host;
    }

    public function generate($path)
    {
        return $this->host.$path;
    }
}

class Controller
{
    public function myAction(Request $request)
    {
        // Instead of
        $page = isset($_GET['page']) ? intval($_GET['page']) : 1;

        // Better (assuming you use the Symfony2 request)
        $page = $request->query->get('page', 1);
    }
}
Loading history...
513
    {
514
        $_SESSION['pfbc'][$this->attributes['id']]['form'] = serialize($this);
515
    }
516
517
    private function renderCSSFiles()
518
    {
519
        $urls = [];
520
        /**
521
         * These files are already included by default in layout.tpl, therefore it is unnecessary to include them again.
522
         *
523
         * if(!in_array('jQueryUI', $this->prevent))
524
         * $urls[] = $this->prefix . '://ajax.googleapis.com/ajax/libs/jqueryui/1/themes/' . $this->jQueryUITheme . '/jquery-ui.css';
525
         */
526
        foreach ($this->elements as $element) {
527
            $elementUrls = $element->getCSSFiles();
528
            if (is_array($elementUrls)) {
529
                $urls = array_merge($urls, $elementUrls);
530
            }
531
        }
532
533
        //*This section prevents duplicate css files from being loaded.*/
534
        if (!empty($urls)) {
535
            $urls = array_values(array_unique($urls));
536
            foreach ($urls as $url) {
537
                echo '<link rel="stylesheet" href="', $url, '"/>';
538
            }
539
        }
540
    }
541
542
    /**
543
     * @return bool
544
     */
545
    private function isNotJQueryUiButtons()
546
    {
547
        return !in_array('jQueryUIButtons', $this->prevent, true);
548
    }
549
550
    /**
551
     * @return bool
552
     */
553
    private function isFormFocusNotOnHomepage()
554
    {
555
        return ((new HttpRequest)->currentUrl() !== PH7_URL_ROOT) &&
556
            !in_array('focus', $this->prevent, true);
557
    }
558
}
559