1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 10:12:41 +01:00

Allow blog resources to be served without Celerity

Summary:
Allow skins to serve arbitrary resources without needing to be mapped, so we can have a vibrant community of amateur skinners.

For "basic" skins, just put all the "css/" on the page always.

Includes an image to prove that works.

@vrana, pretty sure this has no impact outside of Phame but it does change Celerity so it might be to blame if there's any weirdness with static resources.

Test Plan:
{F21341}
{F21340}

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1373

Differential Revision: https://secure.phabricator.com/D3719
This commit is contained in:
epriestley 2012-10-17 08:37:05 -07:00
parent b3ad8507af
commit 26f7425ee2
16 changed files with 341 additions and 52 deletions

View file

@ -1086,7 +1086,7 @@ return array(
// Directories to look for Phame skins inside of.
'phame.skins' => array(
'externals/skin/',
'externals/skins/',
),
// -- Remarkup -------------------------------------------------------------- //

View file

@ -0,0 +1,77 @@
html, body, p, h1, h2, h3 {
padding: 0;
margin: 0;
font-weight: normal;
}
html {
font-family: "Helvetica Neue", "Arial", sans-serif;
font-size: 16px;
overflow-y: scroll;
color: #555555;
}
.oblivious-info {
position: fixed;
width: 15%;
border-right: 1px solid #dfdfdf;
top: 0;
bottom: 0;
left: 0;
padding: 140px 2% 0;
overflow: hidden;
background: url(/image/badge.png);
background-repeat: no-repeat;
background-position: 20px 20px;
}
.oblivious-content {
padding-top: 3%;
margin-left: 22%;
max-width: 600px;
}
a {
color: #222222;
text-decoration: none;
}
a:hover {
color: #a00000;
}
h1 {
font-size: 24px;
font-weight: normal;
}
h2 {
font-size: 22px;
font-weight: bold;
}
.phame-post {
margin: 0 0 2em;
}
.phame-post-date {
font-size: 12px;
margin: .25em 0 1em;
}
.phame-post {
line-height: 1.6em;
}
.phame-post p {
margin: 0 0 1em;
}
.fb-comments,
.fb-comments span,
.fb-comments iframe[style] {
width: 100% !important;
}

View file

@ -2,6 +2,9 @@
<html>
<head>
<title><?php echo _e($title); ?></title>
<?php echo $skin->getCSSResources(); ?>
</head>
<body>
<div class="oblivious-info">

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -91,6 +91,7 @@ phutil_register_library_map(array(
'AphrontView' => 'view/AphrontView.php',
'AphrontWebpageResponse' => 'aphront/response/AphrontWebpageResponse.php',
'CelerityAPI' => 'infrastructure/celerity/CelerityAPI.php',
'CelerityPhabricatorResourceController' => 'infrastructure/celerity/CelerityPhabricatorResourceController.php',
'CelerityResourceController' => 'infrastructure/celerity/CelerityResourceController.php',
'CelerityResourceGraph' => 'infrastructure/celerity/CelerityResourceGraph.php',
'CelerityResourceMap' => 'infrastructure/celerity/CelerityResourceMap.php',
@ -1174,6 +1175,7 @@ phutil_register_library_map(array(
'PhamePostUnpublishController' => 'applications/phame/controller/post/PhamePostUnpublishController.php',
'PhamePostView' => 'applications/phame/view/PhamePostView.php',
'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php',
'PhameResourceController' => 'applications/phame/controller/PhameResourceController.php',
'PhameSkinSpecification' => 'applications/phame/skins/PhameSkinSpecification.php',
'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php',
'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php',
@ -1334,6 +1336,7 @@ phutil_register_library_map(array(
'AphrontTypeaheadTemplateView' => 'AphrontView',
'AphrontUsageException' => 'AphrontException',
'AphrontWebpageResponse' => 'AphrontResponse',
'CelerityPhabricatorResourceController' => 'CelerityResourceController',
'CelerityResourceController' => 'PhabricatorController',
'CelerityResourceGraph' => 'AbstractDirectedGraph',
'CelerityResourceTransformerTestCase' => 'PhabricatorTestCase',
@ -2313,6 +2316,7 @@ phutil_register_library_map(array(
'PhamePostUnpublishController' => 'PhameController',
'PhamePostView' => 'AphrontView',
'PhamePostViewController' => 'PhameController',
'PhameResourceController' => 'CelerityResourceController',
'PhortuneMonthYearExpiryControl' => 'AphrontFormControl',
'PhortuneStripeBaseController' => 'PhabricatorController',
'PhortuneStripePaymentFormView' => 'AphrontView',

View file

@ -155,7 +155,7 @@ class AphrontDefaultApplicationConfiguration
'(?P<package>pkg/)?'.
'(?P<hash>[a-f0-9]{8})/'.
'(?P<path>.+\.(?:css|js|jpg|png|swf|gif))'
=> 'CelerityResourceController',
=> 'CelerityPhabricatorResourceController',
),
);
}

View file

@ -46,6 +46,9 @@ final class PhabricatorApplicationPhame extends PhabricatorApplication {
return array(
'/phame/' => array(
'' => 'PhamePostListController',
'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)'
=> 'PhameResourceController',
'live/(?P<id>[^/]+)/(?P<more>.*)' => 'PhameBlogLiveController',
'post/' => array(
'(?:(?P<filter>draft|all)/)?' => 'PhamePostListController',

View file

@ -0,0 +1,81 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @group phame
*/
final class PhameResourceController extends CelerityResourceController {
private $id;
private $hash;
private $name;
private $root;
protected function getRootDirectory() {
return $this->root;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
$this->hash = $data['hash'];
$this->name = $data['name'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
// We require a visible blog associated with a given skin to serve
// resources, so you can't go fishing around where you shouldn't be.
$blog = id(new PhameBlogQuery())
->setViewer($user)
->withIDs(array($this->id))
->executeOne();
if (!$blog) {
return new Aphront404Response();
}
$skin = $blog->getSkinRenderer($request);
$spec = $skin->getSpecification();
$this->root = $spec->getRootDirectory().DIRECTORY_SEPARATOR;
return $this->serveResource($this->name, $package_hash = null);
}
protected function buildResourceTransformer() {
$xformer = new CelerityResourceTransformer();
$xformer->setMinify(false);
$xformer->setTranslateURICallback(array($this, 'translateResourceURI'));
return $xformer;
}
public function translateResourceURI(array $matches) {
$uri = trim($matches[1], "'\" \r\t\n");
if (Filesystem::pathExists($this->root.$uri)) {
$hash = filemtime($this->root.$uri);
} else {
$hash = '-';
}
$uri = '/phame/r/'.$this->id.'/'.$hash.'/'.$uri;
return 'url('.$uri.')';
}
}

View file

@ -50,19 +50,20 @@ final class PhameBlogLiveController extends PhameController {
->setURI('http://'.$blog->getDomain().'/'.$this->more);
}
if ($blog->getDomain()) {
$base_path = '/';
} else {
$base_path = '/phame/live/'.$blog->getID().'/';
}
$phame_request = clone $request;
$phame_request->setPath('/'.ltrim($this->more, '/'));
if ($blog->getDomain()) {
$uri = new PhutilURI('http://'.$blog->getDomain().'/');
} else {
$uri = '/phame/live/'.$blog->getID().'/';
$uri = PhabricatorEnv::getURI($uri);
}
$skin = $blog->getSkinRenderer($phame_request);
$skin
->setBlog($blog)
->setBaseURI($request->getRequestURI()->setPath($base_path));
->setBaseURI((string)$uri);
$skin->willProcessRequest(array());
return $skin->processRequest();

View file

@ -25,7 +25,7 @@ abstract class PhameBasicBlogSkin extends PhameBlogSkin {
private $pager;
final public function processRequest() {
public function processRequest() {
$request = $this->getRequest();
$content = $this->renderContent($request);
@ -98,6 +98,21 @@ abstract class PhameBasicBlogSkin extends PhameBlogSkin {
return '<h2>404 Not Found</h2>';
}
final public function getResourceURI($resource) {
$root = $this->getSpecification()->getRootDirectory();
$path = $root.DIRECTORY_SEPARATOR.$resource;
$data = Filesystem::readFile($path);
$hash = PhabricatorHash::digest($data);
$hash = substr($hash, 0, 6);
$id = $this->getBlog()->getID();
$uri = '/phame/r/'.$id.'/'.$hash.'/'.$resource;
$uri = PhabricatorEnv::getCDNURI($uri);
return $uri;
}
/* -( Paging )------------------------------------------------------------- */
@ -179,7 +194,7 @@ abstract class PhameBasicBlogSkin extends PhameBlogSkin {
/**
* @task internal
*/
private function renderContent(AphrontRequest $request) {
protected function renderContent(AphrontRequest $request) {
$user = $request->getUser();
$matches = null;

View file

@ -21,11 +21,51 @@
*/
final class PhameBasicTemplateBlogSkin extends PhameBasicBlogSkin {
public function willProcessRequest(array $data) {
private $cssResources;
public function processRequest() {
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/support/phame/libskin.php';
parent::willProcessRequest($data);
$css = $this->getPath('css/');
if (Filesystem::pathExists($css)) {
$this->cssResources = array();
foreach (Filesystem::listDirectory($css) as $path) {
if (!preg_match('/.css$/', $path)) {
continue;
}
$this->cssResources[] = phutil_render_tag(
'link',
array(
'rel' => 'stylesheet',
'type' => 'text/css',
'href' => $this->getResourceURI('css/'.$path),
));
}
$this->cssResources = implode("\n", $this->cssResources);
}
$request = $this->getRequest();
$content = $this->renderContent($request);
if (!$content) {
$content = $this->render404Page();
}
$content = array(
$this->renderHeader(),
$content,
$this->renderFooter(),
);
$response = new AphrontWebpageResponse();
$response->setContent(implode("\n", $content));
return $response;
}
public function getCSSResources() {
return $this->cssResources;
}
public function getName() {
@ -45,7 +85,9 @@ final class PhameBasicTemplateBlogSkin extends PhameBasicBlogSkin {
ob_start();
if (Filesystem::pathExists($this->getPath($__template__))) {
extract($__scope__ + $this->getDefaultScope());
// Fool lint.
$__evil__ = 'extract';
$__evil__($__scope__ + $this->getDefaultScope());
require $this->getPath($__template__);
}

View file

@ -65,6 +65,12 @@ final class PhameBlog extends PhameDAO
self::SKIN_DEFAULT);
}
if (!$spec) {
throw new Exception(
"This blog has an invalid skin, and the default skin failed to ".
"load.");
}
$skin = newv($spec->getSkinClass(), array($request));
$skin->setSpecification($spec);

View file

@ -169,8 +169,7 @@ final class PhamePostView extends AphrontView {
),
$this->renderTitle().
$this->renderDatePublished().
$this->renderSummary().
$this->renderComments());
$this->renderSummary());
}
private function renderFacebookComments() {
@ -198,13 +197,14 @@ final class PhamePostView extends AphrontView {
$c_uri
);
$uri = $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle());
$fb_comments = phutil_render_tag('div',
array(
'class' => 'fb-comments',
'data-href' => $this->getRequestURI(),
'data-href' => $uri,
'data-num-posts' => 5,
'data-width' => 1080, // we hack this to fluid in css
'data-colorscheme' => 'dark',
),
''
);
@ -249,7 +249,7 @@ final class PhamePostView extends AphrontView {
' document.getElementsByTagName("body")[0]).appendChild(dsq);'.
'})(); </script>',
$post->getPHID(),
$this->getRequestURI(),
$this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()),
$post->getTitle()
);

View file

@ -0,0 +1,59 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* Delivers CSS and JS resources to the browser. This controller handles all
* ##/res/## requests, and manages caching, package construction, and resource
* preprocessing.
*
* @group celerity
*/
final class CelerityPhabricatorResourceController
extends CelerityResourceController {
private $path;
private $hash;
private $package;
protected function getRootDirectory() {
$root = dirname(phutil_get_library_root('phabricator'));
return $root.'/webroot/';
}
public function willProcessRequest(array $data) {
$this->path = $data['path'];
$this->hash = $data['hash'];
$this->package = !empty($data['package']);
}
public function processRequest() {
$package_hash = null;
if ($this->package) {
$package_hash = $this->hash;
}
return $this->serveResource($this->path, $package_hash);
}
protected function buildResourceTransformer() {
$xformer = new CelerityResourceTransformer();
$xformer->setMinify(PhabricatorEnv::getEnvConfig('celerity.minify'));
$xformer->setCelerityMap(CelerityResourceMap::getInstance());
return $xformer;
}
}

View file

@ -16,18 +16,13 @@
* limitations under the License.
*/
/**
* Delivers CSS and JS resources to the browser. This controller handles all
* ##/res/## requests, and manages caching, package construction, and resource
* preprocessing.
*
* @group celerity
*/
final class CelerityResourceController extends PhabricatorController {
abstract class CelerityResourceController extends PhabricatorController {
private $path;
private $hash;
private $package;
abstract protected function getRootDirectory();
protected function buildResourceTransformer() {
return null;
}
public function shouldRequireLogin() {
return false;
@ -37,15 +32,11 @@ final class CelerityResourceController extends PhabricatorController {
return false;
}
public function willProcessRequest(array $data) {
$this->path = $data['path'];
$this->hash = $data['hash'];
$this->package = !empty($data['package']);
private function getDiskPath($to_resource = null) {
return $this->getRootDirectory().$to_resource;
}
public function processRequest() {
$path = $this->path;
protected function serveResource($path, $package_hash = null) {
// Sanity checking to keep this from exposing anything sensitive, since it
// ultimately boils down to disk reads.
if (preg_match('@(//|\.\.)@', $path)) {
@ -66,11 +57,9 @@ final class CelerityResourceController extends PhabricatorController {
return $this->makeResponseCacheable(new Aphront304Response());
}
$root = dirname(phutil_get_library_root('phabricator'));
if ($this->package) {
if ($package_hash) {
$map = CelerityResourceMap::getInstance();
$paths = $map->resolvePackage($this->hash);
$paths = $map->resolvePackage($package_hash);
if (!$paths) {
return new Aphront404Response();
}
@ -78,7 +67,8 @@ final class CelerityResourceController extends PhabricatorController {
try {
$data = array();
foreach ($paths as $package_path) {
$data[] = Filesystem::readFile($root.'/webroot/'.$package_path);
$disk_path = $this->getDiskPath($package_path);
$data[] = Filesystem::readFile($disk_path);
}
$data = implode("\n\n", $data);
} catch (Exception $ex) {
@ -86,17 +76,17 @@ final class CelerityResourceController extends PhabricatorController {
}
} else {
try {
$data = Filesystem::readFile($root.'/webroot/'.$path);
$disk_path = $this->getDiskPath($path);
$data = Filesystem::readFile($disk_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);
$xformer = $this->buildResourceTransformer();
if ($xformer) {
$data = $xformer->transformResource($path, $data);
}
$response = new AphrontFileResponse();
$response->setContent($data);
@ -104,7 +94,7 @@ final class CelerityResourceController extends PhabricatorController {
return $this->makeResponseCacheable($response);
}
private function getSupportedResourceTypes() {
protected function getSupportedResourceTypes() {
return array(
'css' => 'text/css; charset=utf-8',
'js' => 'text/javascript; charset=utf-8',

View file

@ -24,6 +24,12 @@ final class CelerityResourceTransformer {
private $minify;
private $rawResourceMap;
private $celerityMap;
private $translateURICallback;
public function setTranslateURICallback($translate_uricallback) {
$this->translateURICallback = $translate_uricallback;
return $this;
}
public function setMinify($minify) {
$this->minify = $minify;
@ -46,8 +52,10 @@ final class CelerityResourceTransformer {
switch ($type) {
case 'css':
$data = preg_replace_callback(
'@url\s*\((\s*[\'"]?/rsrc/.*?)\)@s',
array($this, 'translateResourceURI'),
'@url\s*\((\s*[\'"]?.*?)\)@s',
nonempty(
$this->translateURICallback,
array($this, 'translateResourceURI')),
$data);
break;
}