統計
| ブランチ: | リビジョン:

pictcode / lib / Cake / View / Helper.php @ 635eef61

履歴 | 表示 | アノテート | ダウンロード (28.165 KB)

1
<?php
2
/**
3
 * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
4
 * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
5
 *
6
 * Licensed under The MIT License
7
 * For full copyright and license information, please see the LICENSE.txt
8
 * Redistributions of files must retain the above copyright notice.
9
 *
10
 * @copyright     Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
11
 * @link          http://cakephp.org CakePHP(tm) Project
12
 * @package       Cake.View
13
 * @since         CakePHP(tm) v 0.2.9
14
 * @license       http://www.opensource.org/licenses/mit-license.php MIT License
15
 */
16

    
17
App::uses('Router', 'Routing');
18
App::uses('Hash', 'Utility');
19
App::uses('Inflector', 'Utility');
20

    
21
/**
22
 * Abstract base class for all other Helpers in CakePHP.
23
 * Provides common methods and features.
24
 *
25
 * @package       Cake.View
26
 */
27
class Helper extends Object {
28

    
29
/**
30
 * Settings for this helper.
31
 *
32
 * @var array
33
 */
34
        public $settings = array();
35

    
36
/**
37
 * List of helpers used by this helper
38
 *
39
 * @var array
40
 */
41
        public $helpers = array();
42

    
43
/**
44
 * A helper lookup table used to lazy load helper objects.
45
 *
46
 * @var array
47
 */
48
        protected $_helperMap = array();
49

    
50
/**
51
 * The current theme name if any.
52
 *
53
 * @var string
54
 */
55
        public $theme = null;
56

    
57
/**
58
 * Request object
59
 *
60
 * @var CakeRequest
61
 */
62
        public $request = null;
63

    
64
/**
65
 * Plugin path
66
 *
67
 * @var string
68
 */
69
        public $plugin = null;
70

    
71
/**
72
 * Holds the fields array('field_name' => array('type' => 'string', 'length' => 100),
73
 * primaryKey and validates array('field_name')
74
 *
75
 * @var array
76
 */
77
        public $fieldset = array();
78

    
79
/**
80
 * Holds tag templates.
81
 *
82
 * @var array
83
 */
84
        public $tags = array();
85

    
86
/**
87
 * Holds the content to be cleaned.
88
 *
89
 * @var mixed
90
 */
91
        protected $_tainted = null;
92

    
93
/**
94
 * Holds the cleaned content.
95
 *
96
 * @var mixed
97
 */
98
        protected $_cleaned = null;
99

    
100
/**
101
 * The View instance this helper is attached to
102
 *
103
 * @var View
104
 */
105
        protected $_View;
106

    
107
/**
108
 * A list of strings that should be treated as suffixes, or
109
 * sub inputs for a parent input. This is used for date/time
110
 * inputs primarily.
111
 *
112
 * @var array
113
 */
114
        protected $_fieldSuffixes = array(
115
                'year', 'month', 'day', 'hour', 'min', 'second', 'meridian'
116
        );
117

    
118
/**
119
 * The name of the current model entities are in scope of.
120
 *
121
 * @see Helper::setEntity()
122
 * @var string
123
 */
124
        protected $_modelScope;
125

    
126
/**
127
 * The name of the current model association entities are in scope of.
128
 *
129
 * @see Helper::setEntity()
130
 * @var string
131
 */
132
        protected $_association;
133

    
134
/**
135
 * The dot separated list of elements the current field entity is for.
136
 *
137
 * @see Helper::setEntity()
138
 * @var string
139
 */
140
        protected $_entityPath;
141

    
142
/**
143
 * Minimized attributes
144
 *
145
 * @var array
146
 */
147
        protected $_minimizedAttributes = array(
148
                'allowfullscreen',
149
                'async',
150
                'autofocus',
151
                'autoplay',
152
                'checked',
153
                'compact',
154
                'controls',
155
                'declare',
156
                'default',
157
                'defaultchecked',
158
                'defaultmuted',
159
                'defaultselected',
160
                'defer',
161
                'disabled',
162
                'enabled',
163
                'formnovalidate',
164
                'hidden',
165
                'indeterminate',
166
                'inert',
167
                'ismap',
168
                'itemscope',
169
                'loop',
170
                'multiple',
171
                'muted',
172
                'nohref',
173
                'noresize',
174
                'noshade',
175
                'novalidate',
176
                'nowrap',
177
                'open',
178
                'pauseonexit',
179
                'readonly',
180
                'required',
181
                'reversed',
182
                'scoped',
183
                'seamless',
184
                'selected',
185
                'sortable',
186
                'spellcheck',
187
                'truespeed',
188
                'typemustmatch',
189
                'visible'
190
        );
191

    
192
/**
193
 * Format to attribute
194
 *
195
 * @var string
196
 */
197
        protected $_attributeFormat = '%s="%s"';
198

    
199
/**
200
 * Format to attribute
201
 *
202
 * @var string
203
 */
204
        protected $_minimizedAttributeFormat = '%s="%s"';
205

    
206
/**
207
 * Default Constructor
208
 *
209
 * @param View $View The View this helper is being attached to.
210
 * @param array $settings Configuration settings for the helper.
211
 */
212
        public function __construct(View $View, $settings = array()) {
213
                $this->_View = $View;
214
                $this->request = $View->request;
215
                if ($settings) {
216
                        $this->settings = Hash::merge($this->settings, $settings);
217
                }
218
                if (!empty($this->helpers)) {
219
                        $this->_helperMap = ObjectCollection::normalizeObjectArray($this->helpers);
220
                }
221
        }
222

    
223
/**
224
 * Provide non fatal errors on missing method calls.
225
 *
226
 * @param string $method Method to invoke
227
 * @param array $params Array of params for the method.
228
 * @return void
229
 */
230
        public function __call($method, $params) {
231
                trigger_error(__d('cake_dev', 'Method %1$s::%2$s does not exist', get_class($this), $method), E_USER_WARNING);
232
        }
233

    
234
/**
235
 * Lazy loads helpers. Provides access to deprecated request properties as well.
236
 *
237
 * @param string $name Name of the property being accessed.
238
 * @return mixed Helper or property found at $name
239
 * @deprecated 3.0.0 Accessing request properties through this method is deprecated and will be removed in 3.0.
240
 */
241
        public function __get($name) {
242
                if (isset($this->_helperMap[$name]) && !isset($this->{$name})) {
243
                        $settings = array('enabled' => false) + (array)$this->_helperMap[$name]['settings'];
244
                        $this->{$name} = $this->_View->loadHelper($this->_helperMap[$name]['class'], $settings);
245
                }
246
                if (isset($this->{$name})) {
247
                        return $this->{$name};
248
                }
249
                switch ($name) {
250
                        case 'base':
251
                        case 'here':
252
                        case 'webroot':
253
                        case 'data':
254
                                return $this->request->{$name};
255
                        case 'action':
256
                                return isset($this->request->params['action']) ? $this->request->params['action'] : '';
257
                        case 'params':
258
                                return $this->request;
259
                }
260
        }
261

    
262
/**
263
 * Provides backwards compatibility access for setting values to the request object.
264
 *
265
 * @param string $name Name of the property being accessed.
266
 * @param mixed $value Value to set.
267
 * @return void
268
 * @deprecated 3.0.0 This method will be removed in 3.0
269
 */
270
        public function __set($name, $value) {
271
                switch ($name) {
272
                        case 'base':
273
                        case 'here':
274
                        case 'webroot':
275
                        case 'data':
276
                                $this->request->{$name} = $value;
277
                                return;
278
                        case 'action':
279
                                $this->request->params['action'] = $value;
280
                                return;
281
                }
282
                $this->{$name} = $value;
283
        }
284

    
285
/**
286
 * Finds URL for specified action.
287
 *
288
 * Returns a URL pointing at the provided parameters.
289
 *
290
 * @param string|array $url Either a relative string url like `/products/view/23` or
291
 *    an array of URL parameters. Using an array for URLs will allow you to leverage
292
 *    the reverse routing features of CakePHP.
293
 * @param bool $full If true, the full base URL will be prepended to the result
294
 * @return string Full translated URL with base path.
295
 * @link http://book.cakephp.org/2.0/en/views/helpers.html
296
 */
297
        public function url($url = null, $full = false) {
298
                return h(Router::url($url, $full));
299
        }
300

    
301
/**
302
 * Checks if a file exists when theme is used, if no file is found default location is returned
303
 *
304
 * @param string $file The file to create a webroot path to.
305
 * @return string Web accessible path to file.
306
 */
307
        public function webroot($file) {
308
                $asset = explode('?', $file);
309
                $asset[1] = isset($asset[1]) ? '?' . $asset[1] : null;
310
                $webPath = "{$this->request->webroot}" . $asset[0];
311
                $file = $asset[0];
312

    
313
                if (!empty($this->theme)) {
314
                        $file = trim($file, '/');
315
                        $theme = $this->theme . '/';
316

    
317
                        if (DS === '\\') {
318
                                $file = str_replace('/', '\\', $file);
319
                        }
320

    
321
                        if (file_exists(Configure::read('App.www_root') . 'theme' . DS . $this->theme . DS . $file)) {
322
                                $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
323
                        } else {
324
                                $themePath = App::themePath($this->theme);
325
                                $path = $themePath . 'webroot' . DS . $file;
326
                                if (file_exists($path)) {
327
                                        $webPath = "{$this->request->webroot}theme/" . $theme . $asset[0];
328
                                }
329
                        }
330
                }
331
                if (strpos($webPath, '//') !== false) {
332
                        return str_replace('//', '/', $webPath . $asset[1]);
333
                }
334
                return $webPath . $asset[1];
335
        }
336

    
337
/**
338
 * Generate URL for given asset file. Depending on options passed provides full URL with domain name.
339
 * Also calls Helper::assetTimestamp() to add timestamp to local files
340
 *
341
 * @param string|array $path Path string or URL array
342
 * @param array $options Options array. Possible keys:
343
 *   `fullBase` Return full URL with domain name
344
 *   `pathPrefix` Path prefix for relative URLs
345
 *   `ext` Asset extension to append
346
 *   `plugin` False value will prevent parsing path as a plugin
347
 * @return string Generated URL
348
 */
349
        public function assetUrl($path, $options = array()) {
350
                if (is_array($path)) {
351
                        return $this->url($path, !empty($options['fullBase']));
352
                }
353
                if (strpos($path, '://') !== false) {
354
                        return $path;
355
                }
356
                if (!array_key_exists('plugin', $options) || $options['plugin'] !== false) {
357
                        list($plugin, $path) = $this->_View->pluginSplit($path, false);
358
                }
359
                if (!empty($options['pathPrefix']) && $path[0] !== '/') {
360
                        $path = $options['pathPrefix'] . $path;
361
                }
362
                if (!empty($options['ext']) &&
363
                        strpos($path, '?') === false &&
364
                        substr($path, -strlen($options['ext'])) !== $options['ext']
365
                ) {
366
                        $path .= $options['ext'];
367
                }
368
                if (preg_match('|^([a-z0-9]+:)?//|', $path)) {
369
                        return $path;
370
                }
371
                if (isset($plugin)) {
372
                        $path = Inflector::underscore($plugin) . '/' . $path;
373
                }
374
                $path = $this->_encodeUrl($this->assetTimestamp($this->webroot($path)));
375

    
376
                if (!empty($options['fullBase'])) {
377
                        $path = rtrim(Router::fullBaseUrl(), '/') . '/' . ltrim($path, '/');
378
                }
379
                return $path;
380
        }
381

    
382
/**
383
 * Encodes a URL for use in HTML attributes.
384
 *
385
 * @param string $url The URL to encode.
386
 * @return string The URL encoded for both URL & HTML contexts.
387
 */
388
        protected function _encodeUrl($url) {
389
                $path = parse_url($url, PHP_URL_PATH);
390
                $parts = array_map('rawurldecode', explode('/', $path));
391
                $parts = array_map('rawurlencode', $parts);
392
                $encoded = implode('/', $parts);
393
                return h(str_replace($path, $encoded, $url));
394
        }
395

    
396
/**
397
 * Adds a timestamp to a file based resource based on the value of `Asset.timestamp` in
398
 * Configure. If Asset.timestamp is true and debug > 0, or Asset.timestamp === 'force'
399
 * a timestamp will be added.
400
 *
401
 * @param string $path The file path to timestamp, the path must be inside WWW_ROOT
402
 * @return string Path with a timestamp added, or not.
403
 */
404
        public function assetTimestamp($path) {
405
                $stamp = Configure::read('Asset.timestamp');
406
                $timestampEnabled = $stamp === 'force' || ($stamp === true && Configure::read('debug') > 0);
407
                if ($timestampEnabled && strpos($path, '?') === false) {
408
                        $filepath = preg_replace(
409
                                '/^' . preg_quote($this->request->webroot, '/') . '/',
410
                                '',
411
                                urldecode($path)
412
                        );
413
                        $webrootPath = WWW_ROOT . str_replace('/', DS, $filepath);
414
                        if (file_exists($webrootPath)) {
415
                                //@codingStandardsIgnoreStart
416
                                return $path . '?' . @filemtime($webrootPath);
417
                                //@codingStandardsIgnoreEnd
418
                        }
419
                        $segments = explode('/', ltrim($filepath, '/'));
420
                        if ($segments[0] === 'theme') {
421
                                $theme = $segments[1];
422
                                unset($segments[0], $segments[1]);
423
                                $themePath = App::themePath($theme) . 'webroot' . DS . implode(DS, $segments);
424
                                //@codingStandardsIgnoreStart
425
                                return $path . '?' . @filemtime($themePath);
426
                                //@codingStandardsIgnoreEnd
427
                        } else {
428
                                $plugin = Inflector::camelize($segments[0]);
429
                                if (CakePlugin::loaded($plugin)) {
430
                                        unset($segments[0]);
431
                                        $pluginPath = CakePlugin::path($plugin) . 'webroot' . DS . implode(DS, $segments);
432
                                        //@codingStandardsIgnoreStart
433
                                        return $path . '?' . @filemtime($pluginPath);
434
                                        //@codingStandardsIgnoreEnd
435
                                }
436
                        }
437
                }
438
                return $path;
439
        }
440

    
441
/**
442
 * Used to remove harmful tags from content. Removes a number of well known XSS attacks
443
 * from content. However, is not guaranteed to remove all possibilities. Escaping
444
 * content is the best way to prevent all possible attacks.
445
 *
446
 * @param string|array $output Either an array of strings to clean or a single string to clean.
447
 * @return string|array|null Cleaned content for output
448
 * @deprecated 3.0.0 This method will be removed in 3.0
449
 */
450
        public function clean($output) {
451
                $this->_reset();
452
                if (empty($output)) {
453
                        return null;
454
                }
455
                if (is_array($output)) {
456
                        foreach ($output as $key => $value) {
457
                                $return[$key] = $this->clean($value);
458
                        }
459
                        return $return;
460
                }
461
                $this->_tainted = $output;
462
                $this->_clean();
463
                return $this->_cleaned;
464
        }
465

    
466
/**
467
 * Returns a space-delimited string with items of the $options array. If a key
468
 * of $options array happens to be one of those listed in `Helper::$_minimizedAttributes`
469
 *
470
 * And its value is one of:
471
 *
472
 * - '1' (string)
473
 * - 1 (integer)
474
 * - true (boolean)
475
 * - 'true' (string)
476
 *
477
 * Then the value will be reset to be identical with key's name.
478
 * If the value is not one of these 3, the parameter is not output.
479
 *
480
 * 'escape' is a special option in that it controls the conversion of
481
 *  attributes to their html-entity encoded equivalents. Set to false to disable html-encoding.
482
 *
483
 * If value for any option key is set to `null` or `false`, that option will be excluded from output.
484
 *
485
 * @param array $options Array of options.
486
 * @param array $exclude Array of options to be excluded, the options here will not be part of the return.
487
 * @param string $insertBefore String to be inserted before options.
488
 * @param string $insertAfter String to be inserted after options.
489
 * @return string Composed attributes.
490
 * @deprecated 3.0.0 This method will be moved to HtmlHelper in 3.0
491
 */
492
        protected function _parseAttributes($options, $exclude = null, $insertBefore = ' ', $insertAfter = null) {
493
                if (!is_string($options)) {
494
                        $options = (array)$options + array('escape' => true);
495

    
496
                        if (!is_array($exclude)) {
497
                                $exclude = array();
498
                        }
499

    
500
                        $exclude = array('escape' => true) + array_flip($exclude);
501
                        $escape = $options['escape'];
502
                        $attributes = array();
503

    
504
                        foreach ($options as $key => $value) {
505
                                if (!isset($exclude[$key]) && $value !== false && $value !== null) {
506
                                        $attributes[] = $this->_formatAttribute($key, $value, $escape);
507
                                }
508
                        }
509
                        $out = implode(' ', $attributes);
510
                } else {
511
                        $out = $options;
512
                }
513
                return $out ? $insertBefore . $out . $insertAfter : '';
514
        }
515

    
516
/**
517
 * Formats an individual attribute, and returns the string value of the composed attribute.
518
 * Works with minimized attributes that have the same value as their name such as 'disabled' and 'checked'
519
 *
520
 * @param string $key The name of the attribute to create
521
 * @param string $value The value of the attribute to create.
522
 * @param bool $escape Define if the value must be escaped
523
 * @return string The composed attribute.
524
 * @deprecated 3.0.0 This method will be moved to HtmlHelper in 3.0
525
 */
526
        protected function _formatAttribute($key, $value, $escape = true) {
527
                if (is_array($value)) {
528
                        $value = implode(' ', $value);
529
                }
530
                if (is_numeric($key)) {
531
                        return sprintf($this->_minimizedAttributeFormat, $value, $value);
532
                }
533
                $truthy = array(1, '1', true, 'true', $key);
534
                $isMinimized = in_array($key, $this->_minimizedAttributes);
535
                if ($isMinimized && in_array($value, $truthy, true)) {
536
                        return sprintf($this->_minimizedAttributeFormat, $key, $key);
537
                }
538
                if ($isMinimized) {
539
                        return '';
540
                }
541
                return sprintf($this->_attributeFormat, $key, ($escape ? h($value) : $value));
542
        }
543

    
544
/**
545
 * Returns a string to be used as onclick handler for confirm dialogs.
546
 *
547
 * @param string $message Message to be displayed
548
 * @param string $okCode Code to be executed after user chose 'OK'
549
 * @param string $cancelCode Code to be executed after user chose 'Cancel'
550
 * @param array $options Array of options
551
 * @return string onclick JS code
552
 */
553
        protected function _confirm($message, $okCode, $cancelCode = '', $options = array()) {
554
                $message = json_encode($message);
555
                $confirm = "if (confirm({$message})) { {$okCode} } {$cancelCode}";
556
                if (isset($options['escape']) && $options['escape'] === false) {
557
                        $confirm = h($confirm);
558
                }
559
                return $confirm;
560
        }
561

    
562
/**
563
 * Sets this helper's model and field properties to the dot-separated value-pair in $entity.
564
 *
565
 * @param string $entity A field name, like "ModelName.fieldName" or "ModelName.ID.fieldName"
566
 * @param bool $setScope Sets the view scope to the model specified in $tagValue
567
 * @return void
568
 */
569
        public function setEntity($entity, $setScope = false) {
570
                if ($entity === null) {
571
                        $this->_modelScope = false;
572
                }
573
                if ($setScope === true) {
574
                        $this->_modelScope = $entity;
575
                }
576
                $parts = array_values(Hash::filter(explode('.', $entity)));
577
                if (empty($parts)) {
578
                        return;
579
                }
580
                $count = count($parts);
581
                $lastPart = isset($parts[$count - 1]) ? $parts[$count - 1] : null;
582

    
583
                // Either 'body' or 'date.month' type inputs.
584
                if (($count === 1 && $this->_modelScope && !$setScope) ||
585
                        (
586
                                $count === 2 &&
587
                                in_array($lastPart, $this->_fieldSuffixes) &&
588
                                $this->_modelScope &&
589
                                $parts[0] !== $this->_modelScope
590
                        )
591
                ) {
592
                        $entity = $this->_modelScope . '.' . $entity;
593
                }
594

    
595
                // 0.name, 0.created.month style inputs. Excludes inputs with the modelScope in them.
596
                if ($count >= 2 &&
597
                        is_numeric($parts[0]) &&
598
                        !is_numeric($parts[1]) &&
599
                        $this->_modelScope &&
600
                        strpos($entity, $this->_modelScope) === false
601
                ) {
602
                        $entity = $this->_modelScope . '.' . $entity;
603
                }
604

    
605
                $this->_association = null;
606

    
607
                $isHabtm = (
608
                        isset($this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type']) &&
609
                        $this->fieldset[$this->_modelScope]['fields'][$parts[0]]['type'] === 'multiple'
610
                );
611

    
612
                // habtm models are special
613
                if ($count === 1 && $isHabtm) {
614
                        $this->_association = $parts[0];
615
                        $entity = $parts[0] . '.' . $parts[0];
616
                } else {
617
                        // check for associated model.
618
                        $reversed = array_reverse($parts);
619
                        foreach ($reversed as $i => $part) {
620
                                if ($i > 0 && preg_match('/^[A-Z]/', $part)) {
621
                                        $this->_association = $part;
622
                                        break;
623
                                }
624
                        }
625
                }
626
                $this->_entityPath = $entity;
627
        }
628

    
629
/**
630
 * Returns the entity reference of the current context as an array of identity parts
631
 *
632
 * @return array An array containing the identity elements of an entity
633
 */
634
        public function entity() {
635
                return explode('.', $this->_entityPath);
636
        }
637

    
638
/**
639
 * Gets the currently-used model of the rendering context.
640
 *
641
 * @return string
642
 */
643
        public function model() {
644
                if ($this->_association) {
645
                        return $this->_association;
646
                }
647
                return $this->_modelScope;
648
        }
649

    
650
/**
651
 * Gets the currently-used model field of the rendering context.
652
 * Strips off field suffixes such as year, month, day, hour, min, meridian
653
 * when the current entity is longer than 2 elements.
654
 *
655
 * @return string
656
 */
657
        public function field() {
658
                $entity = $this->entity();
659
                $count = count($entity);
660
                $last = $entity[$count - 1];
661
                if ($count > 2 && in_array($last, $this->_fieldSuffixes)) {
662
                        $last = isset($entity[$count - 2]) ? $entity[$count - 2] : null;
663
                }
664
                return $last;
665
        }
666

    
667
/**
668
 * Generates a DOM ID for the selected element, if one is not set.
669
 * Uses the current View::entity() settings to generate a CamelCased id attribute.
670
 *
671
 * @param array|string $options Either an array of html attributes to add $id into, or a string
672
 *   with a view entity path to get a domId for.
673
 * @param string $id The name of the 'id' attribute.
674
 * @return mixed If $options was an array, an array will be returned with $id set. If a string
675
 *   was supplied, a string will be returned.
676
 */
677
        public function domId($options = null, $id = 'id') {
678
                if (is_array($options) && array_key_exists($id, $options) && $options[$id] === null) {
679
                        unset($options[$id]);
680
                        return $options;
681
                } elseif (!is_array($options) && $options !== null) {
682
                        $this->setEntity($options);
683
                        return $this->domId();
684
                }
685

    
686
                $entity = $this->entity();
687
                $model = array_shift($entity);
688
                $dom = $model . implode('', array_map(array('Inflector', 'camelize'), $entity));
689

    
690
                if (is_array($options) && !array_key_exists($id, $options)) {
691
                        $options[$id] = $dom;
692
                } elseif ($options === null) {
693
                        return $dom;
694
                }
695
                return $options;
696
        }
697

    
698
/**
699
 * Gets the input field name for the current tag. Creates input name attributes
700
 * using CakePHP's data[Model][field] formatting.
701
 *
702
 * @param array|string $options If an array, should be an array of attributes that $key needs to be added to.
703
 *   If a string or null, will be used as the View entity.
704
 * @param string $field Field name.
705
 * @param string $key The name of the attribute to be set, defaults to 'name'
706
 * @return mixed If an array was given for $options, an array with $key set will be returned.
707
 *   If a string was supplied a string will be returned.
708
 */
709
        protected function _name($options = array(), $field = null, $key = 'name') {
710
                if ($options === null) {
711
                        $options = array();
712
                } elseif (is_string($options)) {
713
                        $field = $options;
714
                        $options = 0;
715
                }
716

    
717
                if (!empty($field)) {
718
                        $this->setEntity($field);
719
                }
720

    
721
                if (is_array($options) && array_key_exists($key, $options)) {
722
                        return $options;
723
                }
724

    
725
                switch ($field) {
726
                        case '_method':
727
                                $name = $field;
728
                                break;
729
                        default:
730
                                $name = 'data[' . implode('][', $this->entity()) . ']';
731
                }
732

    
733
                if (is_array($options)) {
734
                        $options[$key] = $name;
735
                        return $options;
736
                }
737
                return $name;
738
        }
739

    
740
/**
741
 * Gets the data for the current tag
742
 *
743
 * @param array|string $options If an array, should be an array of attributes that $key needs to be added to.
744
 *   If a string or null, will be used as the View entity.
745
 * @param string $field Field name.
746
 * @param string $key The name of the attribute to be set, defaults to 'value'
747
 * @return mixed If an array was given for $options, an array with $key set will be returned.
748
 *   If a string was supplied a string will be returned.
749
 */
750
        public function value($options = array(), $field = null, $key = 'value') {
751
                if ($options === null) {
752
                        $options = array();
753
                } elseif (is_string($options)) {
754
                        $field = $options;
755
                        $options = 0;
756
                }
757

    
758
                if (is_array($options) && isset($options[$key])) {
759
                        return $options;
760
                }
761

    
762
                if (!empty($field)) {
763
                        $this->setEntity($field);
764
                }
765
                $result = null;
766
                $data = $this->request->data;
767

    
768
                $entity = $this->entity();
769
                if (!empty($data) && is_array($data) && !empty($entity)) {
770
                        $result = Hash::get($data, implode('.', $entity));
771
                }
772

    
773
                $habtmKey = $this->field();
774
                if (empty($result) && isset($data[$habtmKey][$habtmKey]) && is_array($data[$habtmKey])) {
775
                        $result = $data[$habtmKey][$habtmKey];
776
                } elseif (empty($result) && isset($data[$habtmKey]) && is_array($data[$habtmKey])) {
777
                        if (ClassRegistry::isKeySet($habtmKey)) {
778
                                $model = ClassRegistry::getObject($habtmKey);
779
                                $result = $this->_selectedArray($data[$habtmKey], $model->primaryKey);
780
                        }
781
                }
782

    
783
                if (is_array($options)) {
784
                        if ($result === null && isset($options['default'])) {
785
                                $result = $options['default'];
786
                        }
787
                        unset($options['default']);
788
                }
789

    
790
                if (is_array($options)) {
791
                        $options[$key] = $result;
792
                        return $options;
793
                }
794
                return $result;
795
        }
796

    
797
/**
798
 * Sets the defaults for an input tag. Will set the
799
 * name, value, and id attributes for an array of html attributes.
800
 *
801
 * @param string $field The field name to initialize.
802
 * @param array $options Array of options to use while initializing an input field.
803
 * @return array Array options for the form input.
804
 */
805
        protected function _initInputField($field, $options = array()) {
806
                if ($field !== null) {
807
                        $this->setEntity($field);
808
                }
809
                $options = (array)$options;
810
                $options = $this->_name($options);
811
                $options = $this->value($options);
812
                $options = $this->domId($options);
813
                return $options;
814
        }
815

    
816
/**
817
 * Adds the given class to the element options
818
 *
819
 * @param array $options Array options/attributes to add a class to
820
 * @param string $class The class name being added.
821
 * @param string $key the key to use for class.
822
 * @return array Array of options with $key set.
823
 */
824
        public function addClass($options = array(), $class = null, $key = 'class') {
825
                if (isset($options[$key]) && trim($options[$key])) {
826
                        $options[$key] .= ' ' . $class;
827
                } else {
828
                        $options[$key] = $class;
829
                }
830
                return $options;
831
        }
832

    
833
/**
834
 * Returns a string generated by a helper method
835
 *
836
 * This method can be overridden in subclasses to do generalized output post-processing
837
 *
838
 * @param string $str String to be output.
839
 * @return string
840
 * @deprecated 3.0.0 This method will be removed in future versions.
841
 */
842
        public function output($str) {
843
                return $str;
844
        }
845

    
846
/**
847
 * Before render callback. beforeRender is called before the view file is rendered.
848
 *
849
 * Overridden in subclasses.
850
 *
851
 * @param string $viewFile The view file that is going to be rendered
852
 * @return void
853
 */
854
        public function beforeRender($viewFile) {
855
        }
856

    
857
/**
858
 * After render callback. afterRender is called after the view file is rendered
859
 * but before the layout has been rendered.
860
 *
861
 * Overridden in subclasses.
862
 *
863
 * @param string $viewFile The view file that was rendered.
864
 * @return void
865
 */
866
        public function afterRender($viewFile) {
867
        }
868

    
869
/**
870
 * Before layout callback. beforeLayout is called before the layout is rendered.
871
 *
872
 * Overridden in subclasses.
873
 *
874
 * @param string $layoutFile The layout about to be rendered.
875
 * @return void
876
 */
877
        public function beforeLayout($layoutFile) {
878
        }
879

    
880
/**
881
 * After layout callback. afterLayout is called after the layout has rendered.
882
 *
883
 * Overridden in subclasses.
884
 *
885
 * @param string $layoutFile The layout file that was rendered.
886
 * @return void
887
 */
888
        public function afterLayout($layoutFile) {
889
        }
890

    
891
/**
892
 * Before render file callback.
893
 * Called before any view fragment is rendered.
894
 *
895
 * Overridden in subclasses.
896
 *
897
 * @param string $viewFile The file about to be rendered.
898
 * @return void
899
 */
900
        public function beforeRenderFile($viewFile) {
901
        }
902

    
903
/**
904
 * After render file callback.
905
 * Called after any view fragment is rendered.
906
 *
907
 * Overridden in subclasses.
908
 *
909
 * @param string $viewFile The file just be rendered.
910
 * @param string $content The content that was rendered.
911
 * @return void
912
 */
913
        public function afterRenderFile($viewFile, $content) {
914
        }
915

    
916
/**
917
 * Transforms a recordset from a hasAndBelongsToMany association to a list of selected
918
 * options for a multiple select element
919
 *
920
 * @param string|array $data Data array or model name.
921
 * @param string $key Field name.
922
 * @return array
923
 */
924
        protected function _selectedArray($data, $key = 'id') {
925
                if (!is_array($data)) {
926
                        $model = $data;
927
                        if (!empty($this->request->data[$model][$model])) {
928
                                return $this->request->data[$model][$model];
929
                        }
930
                        if (!empty($this->request->data[$model])) {
931
                                $data = $this->request->data[$model];
932
                        }
933
                }
934
                $array = array();
935
                if (!empty($data)) {
936
                        foreach ($data as $row) {
937
                                if (isset($row[$key])) {
938
                                        $array[$row[$key]] = $row[$key];
939
                                }
940
                        }
941
                }
942
                return empty($array) ? null : $array;
943
        }
944

    
945
/**
946
 * Resets the vars used by Helper::clean() to null
947
 *
948
 * @return void
949
 */
950
        protected function _reset() {
951
                $this->_tainted = null;
952
                $this->_cleaned = null;
953
        }
954

    
955
/**
956
 * Removes harmful content from output
957
 *
958
 * @return void
959
 */
960
        protected function _clean() {
961
                if (get_magic_quotes_gpc()) {
962
                        $this->_cleaned = stripslashes($this->_tainted);
963
                } else {
964
                        $this->_cleaned = $this->_tainted;
965
                }
966

    
967
                $this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
968
                $this->_cleaned = preg_replace('#(&\#*\w+)[\x00-\x20]+;#u', "$1;", $this->_cleaned);
969
                $this->_cleaned = preg_replace('#(&\#x*)([0-9A-F]+);*#iu', "$1$2;", $this->_cleaned);
970
                $this->_cleaned = html_entity_decode($this->_cleaned, ENT_COMPAT, "UTF-8");
971
                $this->_cleaned = preg_replace('#(<[^>]+[\x00-\x20\"\'\/])(on|xmlns)[^>]*>#iUu', "$1>", $this->_cleaned);
972
                $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);
973
                $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);
974
                $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=*([\'\"]*)[\x00-\x20]*-moz-binding[\x00-\x20]*:#iUu', '$1=$2nomozbinding...', $this->_cleaned);
975
                $this->_cleaned = preg_replace('#([a-z]*)[\x00-\x20]*=([\'\"]*)[\x00-\x20]*data[\x00-\x20]*:#Uu', '$1=$2nodata...', $this->_cleaned);
976
                $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*expression[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
977
                $this->_cleaned = preg_replace('#(<[^>]+)style[\x00-\x20]*=[\x00-\x20]*([\`\'\"]*).*behaviour[\x00-\x20]*\([^>]*>#iU', "$1>", $this->_cleaned);
978
                $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);
979
                $this->_cleaned = preg_replace('#</*\w+:\w[^>]*>#i', "", $this->_cleaned);
980
                do {
981
                        $oldstring = $this->_cleaned;
982
                        $this->_cleaned = preg_replace('#</*(applet|meta|xml|blink|link|style|script|embed|object|iframe|frame|frameset|ilayer|layer|bgsound|title|base)[^>]*>#i', "", $this->_cleaned);
983
                } while ($oldstring !== $this->_cleaned);
984
                $this->_cleaned = str_replace(array("&amp;", "&lt;", "&gt;"), array("&amp;amp;", "&amp;lt;", "&amp;gt;"), $this->_cleaned);
985
        }
986

    
987
}