1: <?php
2: 3: 4: 5: 6: 7: 8: 9: 10: 11: 12: 13: 14: 15:
16:
17: App::uses('DispatcherFilter', 'Routing');
18:
19: 20: 21: 22: 23: 24:
25: class AssetDispatcher extends DispatcherFilter {
26:
27: 28: 29: 30: 31: 32:
33: public $priority = 9;
34:
35: 36: 37: 38: 39: 40: 41:
42: public function beforeDispatch(CakeEvent $event) {
43: $url = urldecode($event->data['request']->url);
44: if (strpos($url, '..') !== false || strpos($url, '.') === false) {
45: return;
46: }
47:
48: if ($result = $this->_filterAsset($event)) {
49: $event->stopPropagation();
50: return $result;
51: }
52:
53: $assetFile = $this->_getAssetFile($url);
54: if ($assetFile === null || !file_exists($assetFile)) {
55: return null;
56: }
57: $response = $event->data['response'];
58: $event->stopPropagation();
59:
60: $response->modified(filemtime($assetFile));
61: if ($response->checkNotModified($event->data['request'])) {
62: return $response;
63: }
64:
65: $pathSegments = explode('.', $url);
66: $ext = array_pop($pathSegments);
67:
68: $this->_deliverAsset($response, $assetFile, $ext);
69: return $response;
70: }
71:
72: 73: 74: 75: 76: 77: 78:
79: protected function _filterAsset(CakeEvent $event) {
80: $url = $event->data['request']->url;
81: $response = $event->data['response'];
82: $filters = Configure::read('Asset.filter');
83: $isCss = (
84: strpos($url, 'ccss/') === 0 ||
85: preg_match('#^(theme/([^/]+)/ccss/)|(([^/]+)(?<!css)/ccss)/#i', $url)
86: );
87: $isJs = (
88: strpos($url, 'cjs/') === 0 ||
89: preg_match('#^/((theme/[^/]+)/cjs/)|(([^/]+)(?<!js)/cjs)/#i', $url)
90: );
91:
92: if (($isCss && empty($filters['css'])) || ($isJs && empty($filters['js']))) {
93: $response->statusCode(404);
94: return $response;
95: }
96:
97: if ($isCss) {
98: include WWW_ROOT . DS . $filters['css'];
99: return $response;
100: }
101:
102: if ($isJs) {
103: include WWW_ROOT . DS . $filters['js'];
104: return $response;
105: }
106: }
107:
108: 109: 110: 111: 112: 113:
114: protected function _getAssetFile($url) {
115: $parts = explode('/', $url);
116: if ($parts[0] === 'theme') {
117: $themeName = $parts[1];
118: unset($parts[0], $parts[1]);
119: $fileFragment = implode(DS, $parts);
120: $path = App::themePath($themeName) . 'webroot' . DS;
121: return $path . $fileFragment;
122: }
123:
124: $plugin = Inflector::camelize($parts[0]);
125: if ($plugin && CakePlugin::loaded($plugin)) {
126: unset($parts[0]);
127: $fileFragment = implode(DS, $parts);
128: $pluginWebroot = CakePlugin::path($plugin) . 'webroot' . DS;
129: return $pluginWebroot . $fileFragment;
130: }
131: }
132:
133: 134: 135: 136: 137: 138: 139: 140:
141: protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
142: ob_start();
143: $compressionEnabled = Configure::read('Asset.compress') && $response->compress();
144: if ($response->type($ext) === $ext) {
145: $contentType = 'application/octet-stream';
146: $agent = env('HTTP_USER_AGENT');
147: if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
148: $contentType = 'application/octetstream';
149: }
150: $response->type($contentType);
151: }
152: if (!$compressionEnabled) {
153: $response->header('Content-Length', filesize($assetFile));
154: }
155: $response->cache(filemtime($assetFile));
156: $response->send();
157: ob_clean();
158: if ($ext === 'css' || $ext === 'js') {
159: include $assetFile;
160: } else {
161: readfile($assetFile);
162: }
163:
164: if ($compressionEnabled) {
165: ob_end_flush();
166: }
167: }
168:
169: }
170: