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