~tsep-dev/tsep/0.9-beta

« back to all changes in this revision

Viewing changes to branches/symfony/cake/libs/view/helper.php

  • Committer: geoffreyfishing
  • Date: 2011-01-11 23:46:12 UTC
  • Revision ID: svn-v4:ae0de26e-ed09-4cbe-9a20-e40b4c60ac6c::125
Created a symfony branch for future migration to symfony

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/**
 
3
 * Backend for helpers.
 
4
 *
 
5
 * Internal methods for the Helpers.
 
6
 *
 
7
 * PHP versions 4 and 5
 
8
 *
 
9
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
 
10
 * Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
 
11
 *
 
12
 * Licensed under The MIT License
 
13
 * Redistributions of files must retain the above copyright notice.
 
14
 *
 
15
 * @copyright     Copyright 2005-2010, Cake Software Foundation, Inc. (http://cakefoundation.org)
 
16
 * @link          http://cakephp.org CakePHP(tm) Project
 
17
 * @package       cake
 
18
 * @subpackage    cake.cake.libs.view
 
19
 * @since         CakePHP(tm) v 0.2.9
 
20
 * @license       MIT License (http://www.opensource.org/licenses/mit-license.php)
 
21
 */
 
22
 
 
23
/**
 
24
 * Included libs
 
25
 */
 
26
App::import('Core', 'Overloadable');
 
27
 
 
28
/**
 
29
 * Abstract base class for all other Helpers in CakePHP.
 
30
 * Provides common methods and features.
 
31
 *
 
32
 * @package       cake
 
33
 * @subpackage    cake.cake.libs.view
 
34
 */
 
35
class Helper extends Overloadable {
 
36
 
 
37
/**
 
38
 * List of helpers used by this helper
 
39
 *
 
40
 * @var array
 
41
 */
 
42
        var $helpers = null;
 
43
 
 
44
/**
 
45
 * Base URL
 
46
 *
 
47
 * @var string
 
48
 */
 
49
        var $base = null;
 
50
 
 
51
/**
 
52
 * Webroot path
 
53
 *
 
54
 * @var string
 
55
 */
 
56
        var $webroot = null;
 
57
 
 
58
/**
 
59
 * The current theme name if any.
 
60
 *
 
61
 * @var string
 
62
 */
 
63
        var $theme = null;
 
64
 
 
65
/**
 
66
 * URL to current action.
 
67
 *
 
68
 * @var string
 
69
 */
 
70
        var $here = null;
 
71
 
 
72
/**
 
73
 * Parameter array.
 
74
 *
 
75
 * @var array
 
76
 */
 
77
        var $params = array();
 
78
 
 
79
/**
 
80
 * Current action.
 
81
 *
 
82
 * @var string
 
83
 */
 
84
        var $action = null;
 
85
 
 
86
/**
 
87
 * Plugin path
 
88
 *
 
89
 * @var string
 
90
 */
 
91
        var $plugin = null;
 
92
 
 
93
/**
 
94
 * POST data for models
 
95
 *
 
96
 * @var array
 
97
 */
 
98
        var $data = null;
 
99
 
 
100
/**
 
101
 * List of named arguments
 
102
 *
 
103
 * @var array
 
104
 */
 
105
        var $namedArgs = null;
 
106
 
 
107
/**
 
108
 * URL argument separator character
 
109
 *
 
110
 * @var string
 
111
 */
 
112
        var $argSeparator = null;
 
113
 
 
114
/**
 
115
 * Contains model validation errors of form post-backs
 
116
 *
 
117
 * @access public
 
118
 * @var array
 
119
 */
 
120
        var $validationErrors = null;
 
121
 
 
122
/**
 
123
 * Holds tag templates.
 
124
 *
 
125
 * @access public
 
126
 * @var array
 
127
 */
 
128
        var $tags = array();
 
129
 
 
130
/**
 
131
 * Holds the content to be cleaned.
 
132
 *
 
133
 * @access private
 
134
 * @var mixed
 
135
 */
 
136
        var $__tainted = null;
 
137
 
 
138
/**
 
139
 * Holds the cleaned content.
 
140
 *
 
141
 * @access private
 
142
 * @var mixed
 
143
 */
 
144
        var $__cleaned = null;
 
145
 
 
146
/**
 
147
 * Default overload methods
 
148
 *
 
149
 * @access protected
 
150
 */
 
151
        function get__($name) {}
 
152
        function set__($name, $value) {}
 
153
        function call__($method, $params) {
 
154
                trigger_error(sprintf(__('Method %1$s::%2$s does not exist', true), get_class($this), $method), E_USER_WARNING);
 
155
        }
 
156
 
 
157
/**
 
158
 * Parses tag templates into $this->tags.
 
159
 *
 
160
 * @param $name file name inside app/config to load.
 
161
 * @return array merged tags from config/$name.php
 
162
 * @access public
 
163
 */
 
164
        function loadConfig($name = 'tags') {
 
165
                if (file_exists(CONFIGS . $name .'.php')) {
 
166
                        require(CONFIGS . $name .'.php');
 
167
                        if (isset($tags)) {
 
168
                                $this->tags = array_merge($this->tags, $tags);
 
169
                        }
 
170
                }
 
171
                return $this->tags;
 
172
        }
 
173
 
 
174
/**
 
175
 * Finds URL for specified action.
 
176
 *
 
177
 * Returns a URL pointing at the provided parameters.
 
178
 *
 
179
 * @param mixed $url Either a relative string url like `/products/view/23` or
 
180
 *    an array of url parameters.  Using an array for urls will allow you to leverage
 
181
 *    the reverse routing features of CakePHP.
 
182
 * @param boolean $full If true, the full base URL will be prepended to the result
 
183
 * @return string  Full translated URL with base path.
 
184
 * @access public
 
185
 * @link http://book.cakephp.org/view/1448/url
 
186
 */
 
187
        function url($url = null, $full = false) {
 
188
                return h(Router::url($url, $full));
 
189
        }
 
190
 
 
191
/**
 
192
 * Checks if a file exists when theme is used, if no file is found default location is returned
 
193
 *
 
194
 * @param string $file The file to create a webroot path to.
 
195
 * @return string Web accessible path to file.
 
196
 * @access public
 
197
 */
 
198
        function webroot($file) {
 
199
                $asset = explode('?', $file);
 
200
                $asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
 
201
                $webPath = "{$this->webroot}" . $asset[0];
 
202
                $file = $asset[0];
 
203
 
 
204
                if (!empty($this->theme)) {
 
205
                        $file = trim($file, '/');
 
206
                        $theme = $this->theme . '/';
 
207
 
 
208
                        if (DS === '\\') {
 
209
                                $file = str_replace('/', '\\', $file);
 
210
                        }
 
211
 
 
212
                        if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS  . $file)) {
 
213
                                $webPath = "{$this->webroot}theme/" . $theme . $asset[0];
 
214
                        } else {
 
215
                                $viewPaths = App::path('views');
 
216
 
 
217
                                foreach ($viewPaths as $viewPath) {
 
218
                                        $path = $viewPath . 'themed'. DS . $this->theme . DS  . 'webroot' . DS  . $file;
 
219
 
 
220
                                        if (file_exists($path)) {
 
221
                                                $webPath = "{$this->webroot}theme/" . $theme . $asset[0];
 
222
                                                break;
 
223
                                        }
 
224
                                }
 
225
                        }
 
226
                }
 
227
                if (strpos($webPath, '//') !== false) {
 
228
                        return str_replace('//', '/', $webPath . $asset[1]);
 
229
                }
 
230
                return $webPath . $asset[1];
 
231
        }
 
232
 
 
233
/**
 
234
 * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
 
235
 * Configure.  If Asset.timestamp is true and debug > 0, or Asset.timestamp == 'force'
 
236
 * a timestamp will be added.
 
237
 *
 
238
 * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
 
239
 * @return string Path with a timestamp added, or not.
 
240
 * @access public
 
241
 */
 
242
        function assetTimestamp($path) {
 
243
                $timestampEnabled = (
 
244
                        (Configure::read('Asset.timestamp') === true && Configure::read() > 0) ||
 
245
                        Configure::read('Asset.timestamp') === 'force'
 
246
                );
 
247
                if (strpos($path, '?') === false && $timestampEnabled) {
 
248
                        $filepath = preg_replace('/^' . preg_quote($this->webroot, '/') . '/', '', $path);
 
249
                        $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
 
250
                        if (file_exists($webrootPath)) {
 
251
                                return $path . '?' . @filemtime($webrootPath);
 
252
                        }
 
253
                        $segments = explode('/', ltrim($filepath, '/'));
 
254
                        if ($segments[0] === 'theme') {
 
255
                                $theme = $segments[1];
 
256
                                unset($segments[0], $segments[1]);
 
257
                                $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
 
258
                                return $path . '?' . @filemtime($themePath);
 
259
                        } else {
 
260
                                $plugin = $segments[0];
 
261
                                unset($segments[0]);
 
262
                                $pluginPath = App::pluginPath($plugin) . 'webroot' . DS . implode(DS, $segments);
 
263
                                return $path . '?' . @filemtime($pluginPath);
 
264
                        }
 
265
                }
 
266
                return $path;
 
267
        }
 
268
 
 
269
/**
 
270
 * Used to remove harmful tags from content.  Removes a number of well known XSS attacks
 
271
 * from content.  However, is not guaranteed to remove all possiblities.  Escaping
 
272
 * content is the best way to prevent all possible attacks.
 
273
 *
 
274
 * @param mixed $output Either an array of strings to clean or a single string to clean.
 
275
 * @return cleaned content for output
 
276
 * @access public
 
277
 */
 
278
        function clean($output) {
 
279
                $this->__reset();
 
280
                if (empty($output)) {
 
281
                        return null;
 
282
                }
 
283
                if (is_array($output)) {
 
284
                        foreach ($output as $key => $value) {
 
285
                                $return[$key] = $this->clean($value);
 
286
                        }
 
287
                        return $return;
 
288
                }
 
289
                $this->__tainted = $output;
 
290
                $this->__clean();
 
291
                return $this->__cleaned;
 
292
        }
 
293
 
 
294
/**
 
295
 * Returns a space-delimited string with items of the $options array. If a
 
296
 * key of $options array happens to be one of:
 
297
 *
 
298
 * - 'compact'
 
299
 * - 'checked'
 
300
 * - 'declare'
 
301
 * - 'readonly'
 
302
 * - 'disabled'
 
303
 * - 'selected'
 
304
 * - 'defer'
 
305
 * - 'ismap'
 
306
 * - 'nohref'
 
307
 * - 'noshade'
 
308
 * - 'nowrap'
 
309
 * - 'multiple'
 
310
 * - 'noresize'
 
311
 *
 
312
 * And its value is one of:
 
313
 *
 
314
 * - '1' (string)
 
315
 * - 1 (integer)
 
316
 * - true (boolean)
 
317
 * - 'true' (string)
 
318
 *
 
319
 * Then the value will be reset to be identical with key's name.
 
320
 * If the value is not one of these 3, the parameter is not output.
 
321
 *
 
322
 * 'escape' is a special option in that it controls the conversion of
 
323
 *  attributes to their html-entity encoded equivalents.  Set to false to disable html-encoding.
 
324
 *
 
325
 * If value for any option key is set to `null` or `false`, that option will be excluded from output.
 
326
 *
 
327
 * @param array $options Array of options.
 
328
 * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
 
329
 * @param string $insertBefore String to be inserted before options.
 
330
 * @param string $insertAfter String to be inserted after options.
 
331
 * @return string Composed attributes.
 
332
 * @access public
 
333
 */
 
334
        function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
 
335
                if (is_array($options)) {
 
336
                        $options = array_merge(array('escape' => true), $options);
 
337
 
 
338
                        if (!is_array($exclude)) {
 
339
                                $exclude = array();
 
340
                        }
 
341
                        $keys = array_diff(array_keys($options), array_merge($exclude, array('escape')));
 
342
                        $values = array_intersect_key(array_values($options), $keys);
 
343
                        $escape = $options['escape'];
 
344
                        $attributes = array();
 
345
 
 
346
                        foreach ($keys as $index => $key) {
 
347
                                if ($values[$index] !== false && $values[$index] !== null) {
 
348
                                        $attributes[] = $this->__formatAttribute($key, $values[$index], $escape);
 
349
                                }
 
350
                        }
 
351
                        $out = implode(' ', $attributes);
 
352
                } else {
 
353
                        $out = $options;
 
354
                }
 
355
                return $out ? $insertBefore . $out . $insertAfter : '';
 
356
        }
 
357
 
 
358
/**
 
359
 * Formats an individual attribute, and returns the string value of the composed attribute.
 
360
 * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
 
361
 *
 
362
 * @param string $key The name of the attribute to create
 
363
 * @param string $value The value of the attribute to create.
 
364
 * @return string The composed attribute.
 
365
 * @access private
 
366
 */
 
367
        function __formatAttribute($key, $value, $escape = true) {
 
368
                $attribute = '';
 
369
                $attributeFormat = '%s="%s"';
 
370
                $minimizedAttributes = array('compact', 'checked', 'declare', 'readonly', 'disabled',
 
371
                        'selected', 'defer', 'ismap', 'nohref', 'noshade', 'nowrap', 'multiple', 'noresize');
 
372
                if (is_array($value)) {
 
373
                        $value = '';
 
374
                }
 
375
 
 
376
                if (in_array($key, $minimizedAttributes)) {
 
377
                        if ($value === 1 || $value === true || $value === 'true' || $value === '1' || $value == $key) {
 
378
                                $attribute = sprintf($attributeFormat, $key, $key);
 
379
                        }
 
380
                } else {
 
381
                        $attribute = sprintf($attributeFormat, $key, ($escape ? h($value) : $value));
 
382
                }
 
383
                return $attribute;
 
384
        }
 
385
 
 
386
/**
 
387
 * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
 
388
 *
 
389
 * @param mixed $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
 
390
 * @param boolean $setScope Sets the view scope to the model specified in $tagValue
 
391
 * @return void
 
392
 * @access public
 
393
 */
 
394
        function setEntity($entity, $setScope = false) {
 
395
                $view =& ClassRegistry::getObject('view');
 
396
 
 
397
                if ($setScope) {
 
398
                        $view->modelScope = false;
 
399
                } elseif (!empty($view->entityPath) && $view->entityPath == $entity) {
 
400
                        return;
 
401
                }
 
402
 
 
403
                if ($entity === null) {
 
404
                        $view->model = null;
 
405
                        $view->association = null;
 
406
                        $view->modelId = null;
 
407
                        $view->modelScope = false;
 
408
                        $view->entityPath = null;
 
409
                        return;
 
410
                }
 
411
 
 
412
                $view->entityPath = $entity;
 
413
                $model = $view->model;
 
414
                $sameScope = $hasField = false;
 
415
                $parts = array_values(Set::filter(explode('.', $entity), true));
 
416
 
 
417
                if (empty($parts)) {
 
418
                        return;
 
419
                }
 
420
 
 
421
                $count = count($parts);
 
422
                if ($count === 1) {
 
423
                        $sameScope = true;
 
424
                } else {
 
425
                        if (is_numeric($parts[0])) {
 
426
                                $sameScope = true;
 
427
                        }
 
428
                        $reverse = array_reverse($parts);
 
429
                        $field = array_shift($reverse);
 
430
                        while(!empty($reverse)) {
 
431
                                $subject = array_shift($reverse);
 
432
                                if (is_numeric($subject)) {
 
433
                                        continue;
 
434
                                }
 
435
                                if (ClassRegistry::isKeySet($subject)) {
 
436
                                        $model = $subject;
 
437
                                        break;
 
438
                                }
 
439
                        }
 
440
                }
 
441
 
 
442
                if (ClassRegistry::isKeySet($model)) {
 
443
                        $ModelObj =& ClassRegistry::getObject($model);
 
444
                        for ($i = 0; $i < $count; $i++) {
 
445
                                if (
 
446
                                        is_a($ModelObj, 'Model') && 
 
447
                                        ($ModelObj->hasField($parts[$i]) || 
 
448
                                        array_key_exists($parts[$i], $ModelObj->validate))
 
449
                                ) {
 
450
                                        $hasField = $i;
 
451
                                        if ($hasField === 0 || ($hasField === 1 && is_numeric($parts[0]))) {
 
452
                                                $sameScope = true;
 
453
                                        }
 
454
                                        break;
 
455
                                }
 
456
                        }
 
457
 
 
458
                        if ($sameScope === true && in_array($parts[0], array_keys($ModelObj->hasAndBelongsToMany))) {
 
459
                                $sameScope = false;
 
460
                        }
 
461
                }
 
462
 
 
463
                if (!$view->association && $parts[0] == $view->field && $view->field != $view->model) {
 
464
                        array_unshift($parts, $model);
 
465
                        $hasField = true;
 
466
                }
 
467
                $view->field = $view->modelId = $view->fieldSuffix = $view->association = null;
 
468
 
 
469
                switch (count($parts)) {
 
470
                        case 1:
 
471
                                if ($view->modelScope === false) {
 
472
                                        $view->model = $parts[0];
 
473
                                } else {
 
474
                                        $view->field = $parts[0];
 
475
                                        if ($sameScope === false) {
 
476
                                                $view->association = $parts[0];
 
477
                                        }
 
478
                                }
 
479
                        break;
 
480
                        case 2:
 
481
                                if ($view->modelScope === false) {
 
482
                                        list($view->model, $view->field) = $parts;
 
483
                                } elseif ($sameScope === true && $hasField === 0) {
 
484
                                        list($view->field, $view->fieldSuffix) = $parts;
 
485
                                } elseif ($sameScope === true && $hasField === 1) {
 
486
                                        list($view->modelId, $view->field) = $parts;
 
487
                                } else {
 
488
                                        list($view->association, $view->field) = $parts;
 
489
                                }
 
490
                        break;
 
491
                        case 3:
 
492
                                if ($sameScope === true && $hasField === 1) {
 
493
                                        list($view->modelId, $view->field, $view->fieldSuffix) = $parts;
 
494
                                } elseif ($hasField === 2) {
 
495
                                        list($view->association, $view->modelId, $view->field) = $parts;
 
496
                                } else {
 
497
                                        list($view->association, $view->field, $view->fieldSuffix) = $parts;
 
498
                                }
 
499
                        break;
 
500
                        case 4:
 
501
                                if ($parts[0] === $view->model) {
 
502
                                        list($view->model, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
 
503
                                } else {
 
504
                                        list($view->association, $view->modelId, $view->field, $view->fieldSuffix) = $parts;
 
505
                                }
 
506
                        break;
 
507
                        default:
 
508
                                $reverse = array_reverse($parts);
 
509
 
 
510
                                if ($hasField) {
 
511
                                                $view->field = $field;
 
512
                                                if (!is_numeric($reverse[1]) && $reverse[1] != $model) {
 
513
                                                        $view->field = $reverse[1];
 
514
                                                        $view->fieldSuffix = $field;
 
515
                                                }
 
516
                                }
 
517
                                if (is_numeric($parts[0])) {
 
518
                                        $view->modelId = $parts[0];
 
519
                                } elseif ($view->model == $parts[0] && is_numeric($parts[1])) {
 
520
                                        $view->modelId = $parts[1];
 
521
                                }
 
522
                                $view->association = $model;
 
523
                        break;
 
524
                }
 
525
 
 
526
                if (!isset($view->model) || empty($view->model)) {
 
527
                        $view->model = $view->association;
 
528
                        $view->association = null;
 
529
                } elseif ($view->model === $view->association) {
 
530
                        $view->association = null;
 
531
                }
 
532
 
 
533
                if ($setScope) {
 
534
                        $view->modelScope = true;
 
535
                }
 
536
        }
 
537
 
 
538
/**
 
539
 * Gets the currently-used model of the rendering context.
 
540
 *
 
541
 * @return string
 
542
 * @access public
 
543
 */
 
544
        function model() {
 
545
                $view =& ClassRegistry::getObject('view');
 
546
                if (!empty($view->association)) {
 
547
                        return $view->association;
 
548
                } else {
 
549
                        return $view->model;
 
550
                }
 
551
        }
 
552
 
 
553
/**
 
554
 * Gets the ID of the currently-used model of the rendering context.
 
555
 *
 
556
 * @return mixed
 
557
 * @access public
 
558
 */
 
559
        function modelID() {
 
560
                $view =& ClassRegistry::getObject('view');
 
561
                return $view->modelId;
 
562
        }
 
563
 
 
564
/**
 
565
 * Gets the currently-used model field of the rendering context.
 
566
 *
 
567
 * @return string
 
568
 * @access public
 
569
 */
 
570
        function field() {
 
571
                $view =& ClassRegistry::getObject('view');
 
572
                return $view->field;
 
573
        }
 
574
 
 
575
/**
 
576
 * Returns false if given FORM field has no errors. Otherwise it returns the constant set in
 
577
 * the array Model->validationErrors.
 
578
 *
 
579
 * @param string $model Model name as a string
 
580
 * @param string $field Fieldname as a string
 
581
 * @param integer $modelID Unique index identifying this record within the form
 
582
 * @return boolean True on errors.
 
583
 */
 
584
        function tagIsInvalid($model = null, $field = null, $modelID = null) {
 
585
                $view =& ClassRegistry::getObject('view');
 
586
                $errors = $this->validationErrors;
 
587
                $entity = $view->entity();
 
588
                if (!empty($entity)) {
 
589
                        return Set::extract($errors, join('.', $entity));
 
590
                }
 
591
        }
 
592
 
 
593
/**
 
594
 * Generates a DOM ID for the selected element, if one is not set.
 
595
 * Uses the current View::entity() settings to generate a CamelCased id attribute.
 
596
 *
 
597
 * @param mixed $options Either an array of html attributes to add $id into, or a string
 
598
 *   with a view entity path to get a domId for.
 
599
 * @param string $id The name of the 'id' attribute.
 
600
 * @return mixed If $options was an array, an array will be returned with $id set.  If a string
 
601
 *   was supplied, a string will be returned.
 
602
 * @todo Refactor this method to not have as many input/output options.
 
603
 */
 
604
        function domId($options = null, $id = 'id') {
 
605
                $view =& ClassRegistry::getObject('view');
 
606
 
 
607
                if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
 
608
                        unset($options[$id]);
 
609
                        return $options;
 
610
                } elseif (!is_array($options) && $options !== null) {
 
611
                        $this->setEntity($options);
 
612
                        return $this->domId();
 
613
                }
 
614
 
 
615
                $entity = $view->entity();
 
616
                $model = array_shift($entity);
 
617
                $dom = $model . join('', array_map(array('Inflector', 'camelize'), $entity));
 
618
 
 
619
                if (is_array($options) && !array_key_exists($id, $options)) {
 
620
                        $options[$id] = $dom;
 
621
                } elseif ($options === null) {
 
622
                        return $dom;
 
623
                }
 
624
                return $options;
 
625
        }
 
626
 
 
627
/**
 
628
 * Gets the input field name for the current tag. Creates input name attributes
 
629
 * using CakePHP's data[Model][field] formatting.
 
630
 *
 
631
 * @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
 
632
 *   If a string or null, will be used as the View entity.
 
633
 * @param string $field
 
634
 * @param string $key The name of the attribute to be set, defaults to 'name'
 
635
 * @return mixed If an array was given for $options, an array with $key set will be returned.
 
636
 *   If a string was supplied a string will be returned.
 
637
 * @access protected
 
638
 * @todo Refactor this method to not have as many input/output options.
 
639
 */
 
640
        function _name($options = array(), $field = null, $key = 'name') {
 
641
                $view =& ClassRegistry::getObject('view');
 
642
                if ($options === null) {
 
643
                        $options = array();
 
644
                } elseif (is_string($options)) {
 
645
                        $field = $options;
 
646
                        $options = 0;
 
647
                }
 
648
 
 
649
                if (!empty($field)) {
 
650
                        $this->setEntity($field);
 
651
                }
 
652
 
 
653
                if (is_array($options) && array_key_exists($key, $options)) {
 
654
                        return $options;
 
655
                }
 
656
 
 
657
                switch ($field) {
 
658
                        case '_method':
 
659
                                $name = $field;
 
660
                        break;
 
661
                        default:
 
662
                                $name = 'data[' . implode('][', $view->entity()) . ']';
 
663
                        break;
 
664
                }
 
665
 
 
666
                if (is_array($options)) {
 
667
                        $options[$key] = $name;
 
668
                        return $options;
 
669
                } else {
 
670
                        return $name;
 
671
                }
 
672
        }
 
673
 
 
674
/**
 
675
 * Gets the data for the current tag
 
676
 *
 
677
 * @param mixed $options If an array, should be an array of attributes that $key needs to be added to.
 
678
 *   If a string or null, will be used as the View entity.
 
679
 * @param string $field
 
680
 * @param string $key The name of the attribute to be set, defaults to 'value'
 
681
 * @return mixed If an array was given for $options, an array with $key set will be returned.
 
682
 *   If a string was supplied a string will be returned.
 
683
 * @access public
 
684
 * @todo Refactor this method to not have as many input/output options.
 
685
 */
 
686
        function value($options = array(), $field = null, $key = 'value') {
 
687
                if ($options === null) {
 
688
                        $options = array();
 
689
                } elseif (is_string($options)) {
 
690
                        $field = $options;
 
691
                        $options = 0;
 
692
                }
 
693
 
 
694
                if (is_array($options) && isset($options[$key])) {
 
695
                        return $options;
 
696
                }
 
697
 
 
698
                if (!empty($field)) {
 
699
                        $this->setEntity($field);
 
700
                }
 
701
 
 
702
                $view =& ClassRegistry::getObject('view');
 
703
                $result = null;
 
704
 
 
705
                $entity = $view->entity();
 
706
                if (!empty($this->data) && !empty($entity)) {
 
707
                        $result = Set::extract($this->data, join('.', $entity));
 
708
                }
 
709
 
 
710
                $habtmKey = $this->field();
 
711
                if (empty($result) && isset($this->data[$habtmKey][$habtmKey])) {
 
712
                        $result = $this->data[$habtmKey][$habtmKey];
 
713
                } elseif (empty($result) && isset($this->data[$habtmKey]) && is_array($this->data[$habtmKey])) {
 
714
                        if (ClassRegistry::isKeySet($habtmKey)) {
 
715
                                $model =& ClassRegistry::getObject($habtmKey);
 
716
                                $result = $this->__selectedArray($this->data[$habtmKey], $model->primaryKey);
 
717
                        }
 
718
                }
 
719
 
 
720
                if (is_array($result)) {
 
721
                        if (array_key_exists($view->fieldSuffix, $result)) {
 
722
                                $result = $result[$view->fieldSuffix];
 
723
                        }
 
724
                }
 
725
 
 
726
                if (is_array($options)) {
 
727
                        if ($result === null && isset($options['default'])) {
 
728
                                $result = $options['default'];
 
729
                        }
 
730
                        unset($options['default']);
 
731
                }
 
732
 
 
733
                if (is_array($options)) {
 
734
                        $options[$key] = $result;
 
735
                        return $options;
 
736
                } else {
 
737
                        return $result;
 
738
                }
 
739
        }
 
740
 
 
741
/**
 
742
 * Sets the defaults for an input tag.  Will set the
 
743
 * name, value, and id attributes for an array of html attributes. Will also
 
744
 * add a 'form-error' class if the field contains validation errors.
 
745
 *
 
746
 * @param string $field The field name to initialize.
 
747
 * @param array $options Array of options to use while initializing an input field.
 
748
 * @return array Array options for the form input.
 
749
 * @access protected
 
750
 */
 
751
        function _initInputField($field, $options = array()) {
 
752
                if ($field !== null) {
 
753
                        $this->setEntity($field);
 
754
                }
 
755
                $options = (array)$options;
 
756
                $options = $this->_name($options);
 
757
                $options = $this->value($options);
 
758
                $options = $this->domId($options);
 
759
                if ($this->tagIsInvalid()) {
 
760
                        $options = $this->addClass($options, 'form-error');
 
761
                }
 
762
                return $options;
 
763
        }
 
764
 
 
765
/**
 
766
 * Adds the given class to the element options
 
767
 *
 
768
 * @param array $options Array options/attributes to add a class to
 
769
 * @param string $class The classname being added.
 
770
 * @param string $key the key to use for class.
 
771
 * @return array Array of options with $key set.
 
772
 * @access public
 
773
 */
 
774
        function addClass($options = array(), $class = null, $key = 'class') {
 
775
                if (isset($options[$key]) && trim($options[$key]) != '') {
 
776
                        $options[$key] .= ' ' . $class;
 
777
                } else {
 
778
                        $options[$key] = $class;
 
779
                }
 
780
                return $options;
 
781
        }
 
782
 
 
783
/**
 
784
 * Returns a string generated by a helper method
 
785
 *
 
786
 * This method can be overridden in subclasses to do generalized output post-processing
 
787
 *
 
788
 * @param string $str String to be output.
 
789
 * @return string
 
790
 * @deprecated This method will be removed in future versions.
 
791
 */
 
792
        function output($str) {
 
793
                return $str;
 
794
        }
 
795
 
 
796
/**
 
797
 * Before render callback. beforeRender is called before the view file is rendered.
 
798
 *
 
799
 * Overridden in subclasses.
 
800
 *
 
801
 * @return void
 
802
 * @access public
 
803
 */
 
804
        function beforeRender() {
 
805
        }
 
806
 
 
807
/**
 
808
 * After render callback.  afterRender is called after the view file is rendered
 
809
 * but before the layout has been rendered.
 
810
 *
 
811
 * Overridden in subclasses.
 
812
 *
 
813
 * @return void
 
814
 * @access public
 
815
 */
 
816
        function afterRender() {
 
817
        }
 
818
 
 
819
/**
 
820
 * Before layout callback.  beforeLayout is called before the layout is rendered.
 
821
 *
 
822
 * Overridden in subclasses.
 
823
 *
 
824
 * @return void
 
825
 * @access public
 
826
 */
 
827
        function beforeLayout() {
 
828
        }
 
829
 
 
830
/**
 
831
 * After layout callback.  afterLayout is called after the layout has rendered.
 
832
 *
 
833
 * Overridden in subclasses.
 
834
 *
 
835
 * @return void
 
836
 * @access public
 
837
 */
 
838
        function afterLayout() {
 
839
        }
 
840
 
 
841
/**
 
842
 * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
 
843
 * options for a multiple select element
 
844
 *
 
845
 * @param mixed $data
 
846
 * @param string $key
 
847
 * @return array
 
848
 * @access private
 
849
 */
 
850
        function __selectedArray($data, $key = 'id') {
 
851
                if (!is_array($data)) {
 
852
                        $model = $data;
 
853
                        if (!empty($this->data[$model][$model])) {
 
854
                                return $this->data[$model][$model];
 
855
                        }
 
856
                        if (!empty($this->data[$model])) {
 
857
                                $data = $this->data[$model];
 
858
                        }
 
859
                }
 
860
                $array = array();
 
861
                if (!empty($data)) {
 
862
                        foreach ($data as $var) {
 
863
                                $array[$var[$key]] = $var[$key];
 
864
                        }
 
865
                }
 
866
                return $array;
 
867
        }
 
868
 
 
869
/**
 
870
 * Resets the vars used by Helper::clean() to null
 
871
 *
 
872
 * @return void
 
873
 * @access private
 
874
 */
 
875
        function __reset() {
 
876
                $this->__tainted = null;
 
877
                $this->__cleaned = null;
 
878
        }
 
879
 
 
880
/**
 
881
 * Removes harmful content from output
 
882
 *
 
883
 * @return void
 
884
 * @access private
 
885
 */
 
886
        function __clean() {
 
887
                if (get_magic_quotes_gpc()) {
 
888
                        $this->__cleaned = stripslashes($this->__tainted);
 
889
                } else {
 
890
                        $this->__cleaned = $this->__tainted;
 
891
                }
 
892
 
 
893
                $this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
 
894
                $this->__cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->__cleaned);
 
895
                $this->__cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->__cleaned);
 
896
                $this->__cleaned = html_entity_decode($this->__cleaned, ENT_COMPAT, "UTF-8");
 
897
                $this->__cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->__cleaned);
 
898
                $this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*)[\\x00-\x20]*j[\x00-\x20]*a[\x00-\x20]*v[\x00-\x20]*a[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2nojavascript...', $this->__cleaned);
 
899
                $this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*v[\x00-\x20]*b[\x00-\x20]*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:#iUu', '$1=$2novbscript...', $this->__cleaned);
 
900
                $this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu','$1=$2nomozbinding...', $this->__cleaned);
 
901
                $this->__cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->__cleaned);
 
902
                $this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
 
903
                $this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->__cleaned);
 
904
                $this->__cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*s[\x00-\x20]*c[\x00-\x20]*r[\x00-\x20]*i[\x00-\x20]*p[\x00-\x20]*t[\x00-\x20]*:*[^>]*>#iUu', "$1>", $this->__cleaned);
 
905
                $this->__cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->__cleaned);
 
906
                do {
 
907
                        $oldstring = $this->__cleaned;
 
908
                        $this->__cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->__cleaned);
 
909
                } while ($oldstring != $this->__cleaned);
 
910
                $this->__cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->__cleaned);
 
911
        }
 
912
}