protected function buildResourceTransformer() { $xformer = new CelerityResourceTransformer(); $xformer->setMinify(false); $xformer->setTranslateURICallback(array($this, 'translateResourceURI')); return $xformer; }
public function processRequest() { $path = $this->path; // Sanity checking to keep this from exposing anything sensitive, since it // ultimately boils down to disk reads. if (preg_match('@(//|\\.\\.)@', $path)) { return new Aphront400Response(); } $type = CelerityResourceTransformer::getResourceType($path); $type_map = $this->getSupportedResourceTypes(); if (empty($type_map[$type])) { throw new Exception("Only static resources may be served."); } if (isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) && !PhabricatorEnv::getEnvConfig('celerity.force-disk-reads')) { // Return a "304 Not Modified". We don't care about the value of this // field since we never change what resource is served by a given URI. return $this->makeResponseCacheable(new Aphront304Response()); } $root = dirname(phutil_get_library_root('phabricator')); if ($this->package) { $map = CelerityResourceMap::getInstance(); $paths = $map->resolvePackage($this->hash); if (!$paths) { return new Aphront404Response(); } try { $data = array(); foreach ($paths as $package_path) { $data[] = Filesystem::readFile($root . '/webroot/' . $package_path); } $data = implode("\n\n", $data); } catch (Exception $ex) { return new Aphront404Response(); } } else { try { $data = Filesystem::readFile($root . '/webroot/' . $path); } catch (Exception $ex) { return new Aphront404Response(); } } $xformer = new CelerityResourceTransformer(); $xformer->setMinify(PhabricatorEnv::getEnvConfig('celerity.minify')); $xformer->setCelerityMap(CelerityResourceMap::getInstance()); $data = $xformer->transformResource($path, $data); $response = new AphrontFileResponse(); $response->setContent($data); $response->setMimeType($type_map[$type]); return $this->makeResponseCacheable($response); }
public function testTransformation() { $files = dirname(__FILE__) . '/transformer/'; foreach (Filesystem::listDirectory($files) as $file) { $name = basename($file); $data = Filesystem::readFile($files . '/' . $file); $parts = preg_split('/^~~~+\\n/m', $data); $parts = array_merge($parts, array(null)); list($options, $in, $expect) = $parts; $options = PhutilSimpleOptions::parse($options) + array('minify' => false, 'name' => $name); $xformer = new CelerityResourceTransformer(); $xformer->setRawResourceMap(array('/rsrc/example.png' => array('uri' => '/res/hash/example.png'))); $xformer->setMinify($options['minify']); $result = $xformer->transformResource($options['name'], $in); $this->assertEqual(rtrim($expect), rtrim($result), $file); } }
private function composeImage($color, $icon_data) { $icon_img = imagecreatefromstring($icon_data); $map = CelerityResourceTransformer::getCSSVariableMap(); $color_string = idx($map, $color, '#ff00ff'); $color_const = hexdec(trim($color_string, '#')); $canvas = imagecreatetruecolor(100, 100); imagefill($canvas, 0, 0, $color_const); imagecopy($canvas, $icon_img, 0, 0, 0, 0, 100, 100); return PhabricatorImageTransformer::saveImageDataInAnyFormat($canvas, 'image/png'); }
protected function serveResource(array $spec) { $path = $spec['path']; $hash = idx($spec, 'hash'); // Sanity checking to keep this from exposing anything sensitive, since it // ultimately boils down to disk reads. if (preg_match('@(//|\\.\\.)@', $path)) { return new Aphront400Response(); } $type = CelerityResourceTransformer::getResourceType($path); $type_map = self::getSupportedResourceTypes(); if (empty($type_map[$type])) { throw new Exception(pht('Only static resources may be served.')); } $dev_mode = PhabricatorEnv::getEnvConfig('phabricator.developer-mode'); $map = $this->getCelerityResourceMap(); $expect_hash = $map->getHashForName($path); // Test if the URI hash is correct for our current resource map. If it // is not, refuse to cache this resource. This avoids poisoning caches // and CDNs if we're getting a request for a new resource to an old node // shortly after a push. $is_cacheable = $hash === $expect_hash; $is_locally_cacheable = $this->isLocallyCacheableResourceType($type); if (AphrontRequest::getHTTPHeader('If-Modified-Since') && $is_cacheable) { // Return a "304 Not Modified". We don't care about the value of this // field since we never change what resource is served by a given URI. return $this->makeResponseCacheable(new Aphront304Response()); } $cache = null; $data = null; if ($is_cacheable && $is_locally_cacheable && !$dev_mode) { $cache = PhabricatorCaches::getImmutableCache(); $request_path = $this->getRequest()->getPath(); $cache_key = $this->getCacheKey($request_path); $data = $cache->getKey($cache_key); } if ($data === null) { if ($map->isPackageResource($path)) { $resource_names = $map->getResourceNamesForPackageName($path); if (!$resource_names) { return new Aphront404Response(); } try { $data = array(); foreach ($resource_names as $resource_name) { $data[] = $map->getResourceDataForName($resource_name); } $data = implode("\n\n", $data); } catch (Exception $ex) { return new Aphront404Response(); } } else { try { $data = $map->getResourceDataForName($path); } catch (Exception $ex) { return new Aphront404Response(); } } $xformer = $this->buildResourceTransformer(); if ($xformer) { $data = $xformer->transformResource($path, $data); } if ($cache) { $cache->setKey($cache_key, $data); } } $response = new AphrontFileResponse(); $response->setContent($data); $response->setMimeType($type_map[$type]); // NOTE: This is a piece of magic required to make WOFF fonts work in // Firefox and IE. Possibly we should generalize this more. $cross_origin_types = array('woff' => true, 'woff2' => true, 'eot' => true); if (isset($cross_origin_types[$type])) { // We could be more tailored here, but it's not currently trivial to // generate a comprehensive list of valid origins (an install may have // arbitrarily many Phame blogs, for example), and we lose nothing by // allowing access from anywhere. $response->addAllowOrigin('*'); } if ($is_cacheable) { $response = $this->makeResponseCacheable($response); } return $response; }
$file_map = array(); foreach ($files as $path => $raw_hash) { echo "."; $path = '/' . Filesystem::readablePath($path, $root); $data = Filesystem::readFile($root . $path); $data = $xformer->transformResource($path, $data); $hash = md5($data); $hash = md5($hash . $path . $resource_hash); $file_map[$path] = array('hash' => $hash, 'disk' => $path); } echo "\n"; $resource_graph = array(); $hash_map = array(); $parser = new PhutilDocblockParser(); foreach ($file_map as $path => $info) { $type = CelerityResourceTransformer::getResourceType($path); $data = Filesystem::readFile($root . $info['disk']); $matches = array(); $ok = preg_match('@/[*][*].*?[*]/@s', $data, $matches); if (!$ok) { throw new Exception("File {$path} does not have a header doc comment. Encode dependency " . "data in a header docblock."); } list($description, $metadata) = $parser->parse($matches[0]); $provides = preg_split('/\\s+/', trim(idx($metadata, 'provides'))); $requires = preg_split('/\\s+/', trim(idx($metadata, 'requires'))); $provides = array_filter($provides); $requires = array_filter($requires); if (!$provides) { // Tests and documentation-only JS is permitted to @provide no targets. continue; }
public function getResourceType($path) { return CelerityResourceTransformer::getResourceType($path); }
/** * Find text resources (like JS and CSS) and return information about them. * * @param CelerityPhysicalResources Resource map to find text resources for. * @param CelerityResourceTransformer Configured resource transformer. * @return map<string, map<string, string>> Resource information map. */ private function rebuildTextResources(CelerityPhysicalResources $resources, CelerityResourceTransformer $xformer) { $text_map = $resources->findTextResources(); $result_map = array(); foreach ($text_map as $name => $data_hash) { $raw_data = $resources->getResourceData($name); $xformed_data = $xformer->transformResource($name, $raw_data); $data_hash = $resources->getCelerityHash($xformed_data); $hash = $resources->getCelerityHash($data_hash . $name); list($provides, $requires) = $this->getProvidesAndRequires($name, $raw_data); $result_map[$name] = array('hash' => $hash); if ($provides !== null) { $result_map[$name] += array('provides' => $provides, 'requires' => $requires); } } return $result_map; }