From b3ad8507af3306ec879b75f37f8fa958b9c4def7 Mon Sep 17 00:00:00 2001 From: epriestley Date: Wed, 17 Oct 2012 08:36:48 -0700 Subject: [PATCH] Allow simple template-based skin definitions Summary: Lower the barrier to entry for installing and creating skins, so we can kill Wordpress. You can now install skins by dropping them into a directory, and build either "advanced" (full phutil library) skins or "basic" (simple PHP templates) skins. Next up is getting static resources working in an easy way for skins. I put these in `externals/` for now so they don't get hit by lint. Test Plan: Viewed the Pokeblog with the Oblivious skin. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T1373 Differential Revision: https://secure.phabricator.com/D3717 --- conf/default.conf.php | 5 + externals/skins/oblivious/404.php | 1 + externals/skins/oblivious/footer.php | 3 + externals/skins/oblivious/header.php | 15 ++ externals/skins/oblivious/post-detail.php | 1 + externals/skins/oblivious/post-list.php | 13 ++ externals/skins/oblivious/skin.json | 3 + src/__phutil_library_map__.php | 5 +- .../blog/PhameBlogEditController.php | 5 +- .../blog/PhameBlogLiveController.php | 1 + .../phame/skins/PhameBasicBlogSkin.php | 7 + .../skins/PhameBasicTemplateBlogSkin.php | 97 +++++++++ .../phame/skins/PhameBlogSkin.php | 10 + .../phame/skins/PhameBlogSkinTenEleven.php | 54 ----- .../phame/skins/PhameSkinSpecification.php | 190 ++++++++++++++++++ src/applications/phame/storage/PhameBlog.php | 17 +- support/phame/libskin.php | 21 ++ 17 files changed, 384 insertions(+), 64 deletions(-) create mode 100644 externals/skins/oblivious/404.php create mode 100644 externals/skins/oblivious/footer.php create mode 100644 externals/skins/oblivious/header.php create mode 100644 externals/skins/oblivious/post-detail.php create mode 100644 externals/skins/oblivious/post-list.php create mode 100644 externals/skins/oblivious/skin.json create mode 100644 src/applications/phame/skins/PhameBasicTemplateBlogSkin.php delete mode 100644 src/applications/phame/skins/PhameBlogSkinTenEleven.php create mode 100644 src/applications/phame/skins/PhameSkinSpecification.php create mode 100644 support/phame/libskin.php diff --git a/conf/default.conf.php b/conf/default.conf.php index 1df780db15..ebcaf8761e 100644 --- a/conf/default.conf.php +++ b/conf/default.conf.php @@ -1084,6 +1084,11 @@ return array( // Information on shortnames - http://docs.disqus.com/help/68/ 'disqus.shortname' => null, + // Directories to look for Phame skins inside of. + 'phame.skins' => array( + 'externals/skin/', + ), + // -- Remarkup -------------------------------------------------------------- // // If you enable this, linked YouTube videos will be embeded inline. This has diff --git a/externals/skins/oblivious/404.php b/externals/skins/oblivious/404.php new file mode 100644 index 0000000000..fc208f6d4d --- /dev/null +++ b/externals/skins/oblivious/404.php @@ -0,0 +1 @@ +

404 Not Found

diff --git a/externals/skins/oblivious/footer.php b/externals/skins/oblivious/footer.php new file mode 100644 index 0000000000..5b6e2d6530 --- /dev/null +++ b/externals/skins/oblivious/footer.php @@ -0,0 +1,3 @@ + + + diff --git a/externals/skins/oblivious/header.php b/externals/skins/oblivious/header.php new file mode 100644 index 0000000000..93945f901c --- /dev/null +++ b/externals/skins/oblivious/header.php @@ -0,0 +1,15 @@ + + + + <?php echo _e($title); ?> + + +
+

+ getName()); + ?> +

+

getDescription()); ?>

+
+
diff --git a/externals/skins/oblivious/post-detail.php b/externals/skins/oblivious/post-detail.php new file mode 100644 index 0000000000..3ca30e9375 --- /dev/null +++ b/externals/skins/oblivious/post-detail.php @@ -0,0 +1 @@ +render(); ?> diff --git a/externals/skins/oblivious/post-list.php b/externals/skins/oblivious/post-list.php new file mode 100644 index 0000000000..1bcb65b28d --- /dev/null +++ b/externals/skins/oblivious/post-list.php @@ -0,0 +1,13 @@ +
+ renderWithSummary(); + } + + ?> +
+
+ + +
diff --git a/externals/skins/oblivious/skin.json b/externals/skins/oblivious/skin.json new file mode 100644 index 0000000000..501ad7c2da --- /dev/null +++ b/externals/skins/oblivious/skin.json @@ -0,0 +1,3 @@ +{ + "name": "Oblivious" +} diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 6ee3882191..3b27e630d5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1150,6 +1150,7 @@ phutil_register_library_map(array( 'PhabricatorXHProfSampleListController' => 'applications/xhprof/controller/PhabricatorXHProfSampleListController.php', 'PhabricatorXHProfSampleListView' => 'applications/xhprof/view/PhabricatorXHProfSampleListView.php', 'PhameBasicBlogSkin' => 'applications/phame/skins/PhameBasicBlogSkin.php', + 'PhameBasicTemplateBlogSkin' => 'applications/phame/skins/PhameBasicTemplateBlogSkin.php', 'PhameBlog' => 'applications/phame/storage/PhameBlog.php', 'PhameBlogDeleteController' => 'applications/phame/controller/blog/PhameBlogDeleteController.php', 'PhameBlogEditController' => 'applications/phame/controller/blog/PhameBlogEditController.php', @@ -1157,7 +1158,6 @@ phutil_register_library_map(array( 'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php', 'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php', 'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php', - 'PhameBlogSkinTenEleven' => 'applications/phame/skins/PhameBlogSkinTenEleven.php', 'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php', 'PhameController' => 'applications/phame/controller/PhameController.php', 'PhameDAO' => 'applications/phame/storage/PhameDAO.php', @@ -1174,6 +1174,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', + 'PhameSkinSpecification' => 'applications/phame/skins/PhameSkinSpecification.php', 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', 'PhortuneStripeBaseController' => 'applications/phortune/stripe/controller/PhortuneStripeBaseController.php', 'PhortuneStripePaymentFormView' => 'applications/phortune/stripe/view/PhortuneStripePaymentFormView.php', @@ -2278,6 +2279,7 @@ phutil_register_library_map(array( 'PhabricatorXHProfSampleListController' => 'PhabricatorXHProfController', 'PhabricatorXHProfSampleListView' => 'AphrontView', 'PhameBasicBlogSkin' => 'PhameBlogSkin', + 'PhameBasicTemplateBlogSkin' => 'PhameBasicBlogSkin', 'PhameBlog' => array( 0 => 'PhameDAO', @@ -2290,7 +2292,6 @@ phutil_register_library_map(array( 'PhameBlogLiveController' => 'PhameController', 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhameBlogSkin' => 'PhabricatorController', - 'PhameBlogSkinTenEleven' => 'PhameBasicBlogSkin', 'PhameBlogViewController' => 'PhameController', 'PhameController' => 'PhabricatorController', 'PhameDAO' => 'PhabricatorLiskDAO', diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index 4b953d2bd0..f8e4017340 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -118,6 +118,9 @@ final class PhameBlogEditController ->setObject($blog) ->execute(); + $skins = PhameSkinSpecification::loadAllSkinSpecifications(); + $skins = mpull($skins, 'getName'); + $form = id(new AphrontFormView()) ->setUser($user) ->setFlexible(true) @@ -171,7 +174,7 @@ final class PhameBlogEditController ->setLabel('Skin') ->setName('skin') ->setValue($blog->getSkin()) - ->setOptions(PhameBlog::getSkinOptionsForSelect()) + ->setOptions($skins) ) ->appendChild( id(new AphrontFormSubmitControl()) diff --git a/src/applications/phame/controller/blog/PhameBlogLiveController.php b/src/applications/phame/controller/blog/PhameBlogLiveController.php index da9c8d6a20..8f4e5976f5 100644 --- a/src/applications/phame/controller/blog/PhameBlogLiveController.php +++ b/src/applications/phame/controller/blog/PhameBlogLiveController.php @@ -64,6 +64,7 @@ final class PhameBlogLiveController extends PhameController { ->setBlog($blog) ->setBaseURI($request->getRequestURI()->setPath($base_path)); + $skin->willProcessRequest(array()); return $skin->processRequest(); } diff --git a/src/applications/phame/skins/PhameBasicBlogSkin.php b/src/applications/phame/skins/PhameBasicBlogSkin.php index b177420488..aa797dc442 100644 --- a/src/applications/phame/skins/PhameBasicBlogSkin.php +++ b/src/applications/phame/skins/PhameBasicBlogSkin.php @@ -54,6 +54,7 @@ abstract class PhameBasicBlogSkin extends PhameBlogSkin { $response = new AphrontWebpageResponse(); $response->setContent($view->render()); + return $response; } @@ -127,6 +128,9 @@ abstract class PhameBasicBlogSkin extends PhameBlogSkin { */ protected function renderOlderPageLink() { $uri = $this->getOlderPageURI(); + if (!$uri) { + return null; + } return phutil_render_tag( 'a', array( @@ -156,6 +160,9 @@ abstract class PhameBasicBlogSkin extends PhameBlogSkin { */ protected function renderNewerPageLink() { $uri = $this->getNewerPageURI(); + if (!$uri) { + return null; + } return phutil_render_tag( 'a', array( diff --git a/src/applications/phame/skins/PhameBasicTemplateBlogSkin.php b/src/applications/phame/skins/PhameBasicTemplateBlogSkin.php new file mode 100644 index 0000000000..dfee960423 --- /dev/null +++ b/src/applications/phame/skins/PhameBasicTemplateBlogSkin.php @@ -0,0 +1,97 @@ +getSpecification()->getName(); + } + + public function getPath($to_file = null) { + $path = $this->getSpecification()->getRootDirectory(); + if ($to_file) { + $path = $path.DIRECTORY_SEPARATOR.$to_file; + } + return $path; + } + + private function renderTemplate($__template__, array $__scope__) { + chdir($this->getPath()); + ob_start(); + + if (Filesystem::pathExists($this->getPath($__template__))) { + extract($__scope__ + $this->getDefaultScope()); + require $this->getPath($__template__); + } + + return ob_get_clean(); + } + + private function getDefaultScope() { + return array( + 'skin' => $this, + 'blog' => $this->getBlog(), + 'uri' => $this->getURI(''), + ); + } + + protected function renderHeader() { + return $this->renderTemplate( + 'header.php', + array( + 'title' => $this->getBlog()->getName(), + )); + } + + protected function renderFooter() { + return $this->renderTemplate('footer.php', array()); + } + + protected function render404Page() { + return $this->renderTemplate('404.php', array()); + } + + protected function renderPostDetail(PhamePostView $post) { + return $this->renderTemplate( + 'post-detail.php', + array( + 'post' => $post, + )); + } + + protected function renderPostList(array $posts) { + return $this->renderTemplate( + 'post-list.php', + array( + 'posts' => $posts, + 'older' => $this->renderNewerPageLink(), + 'newer' => $this->renderOlderPageLink(), + )); + } + +} diff --git a/src/applications/phame/skins/PhameBlogSkin.php b/src/applications/phame/skins/PhameBlogSkin.php index f0cc884434..fcd7de16dd 100644 --- a/src/applications/phame/skins/PhameBlogSkin.php +++ b/src/applications/phame/skins/PhameBlogSkin.php @@ -24,6 +24,16 @@ abstract class PhameBlogSkin extends PhabricatorController { private $blog; private $baseURI; private $preview; + private $specification; + + public function setSpecification(PhameSkinSpecification $specification) { + $this->specification = $specification; + return $this; + } + + public function getSpecification() { + return $this->specification; + } public function setPreview($preview) { $this->preview = $preview; diff --git a/src/applications/phame/skins/PhameBlogSkinTenEleven.php b/src/applications/phame/skins/PhameBlogSkinTenEleven.php deleted file mode 100644 index 463df3ecc1..0000000000 --- a/src/applications/phame/skins/PhameBlogSkinTenEleven.php +++ /dev/null @@ -1,54 +0,0 @@ -getBlog()->getName(); - $about = $this->getBlog()->getDescription(); - - $about = phutil_escape_html($about); - - return << -
-

{$blog_name}

-
{$about}
-
-
- -
-
-EOHTML; - } - - protected function renderFooter() { - return << -
-EOHTML; - } - -} diff --git a/src/applications/phame/skins/PhameSkinSpecification.php b/src/applications/phame/skins/PhameSkinSpecification.php new file mode 100644 index 0000000000..114fb198c9 --- /dev/null +++ b/src/applications/phame/skins/PhameSkinSpecification.php @@ -0,0 +1,190 @@ +setName($name); + + if (isset($specs[$name])) { + $that_dir = $specs[$name]->getRootDirectory(); + $this_dir = $spec->getRootDirectory(); + throw new Exception( + "Two skins have the same name ('{$name}'), in '{$this_dir}' and ". + "'{$that_dir}'. Rename one or adjust your 'phame.skins' ". + "configuration."); + } + + $specs[$name] = $spec; + } + } + } + + return $specs; + } + + public static function loadOneSkinSpecification($name) { + $paths = PhabricatorEnv::getEnvConfig('phame.skins'); + $base = dirname(phutil_get_library_root('phabricator')); + foreach ($paths as $path) { + $path = Filesystem::resolvePath($path, $base); + $skin_path = $path.DIRECTORY_SEPARATOR.$name; + if (is_dir($skin_path)) { + $spec = self::loadSkinSpecification($skin_path); + if ($spec) { + $spec->setName($name); + return $spec; + } + } + } + return null; + } + + public static function loadSkinSpecification($path) { + + $config_path = $path.DIRECTORY_SEPARATOR.'skin.json'; + $config = array(); + if (Filesystem::pathExists($config_path)) { + $config = Filesystem::readFile($config_path); + $config = json_decode($config, true); + if (!is_array($config)) { + throw new Exception( + "Skin configuration file '{$config_path}' is not a valid JSON file."); + } + $type = idx($config, 'type', self::TYPE_BASIC); + } else { + $type = self::TYPE_BASIC; + } + + $spec = new PhameSkinSpecification(); + $spec->setRootDirectory($path); + $spec->setConfig($config); + + switch ($type) { + case self::TYPE_BASIC: + $spec->setSkinClass('PhameBasicTemplateBlogSkin'); + break; + case self::TYPE_ADVANCED: + $spec->setSkinClass($config['class']); + $spec->addPhutilLibrary($path.DIRECTORY_SEPARATOR.'src'); + break; + default: + throw new Exception("Unknown skin type!"); + } + + $spec->setType($type); + + return $spec; + } + + public function setConfig(array $config) { + $this->config = $config; + return $this; + } + + public function getConfig($key, $default = null) { + return idx($this->config, $key, $default); + } + + public function setName($name) { + $this->name = $name; + return $this; + } + + public function getName() { + return $this->getConfig('name', $this->name); + } + + public function setRootDirectory($root_directory) { + $this->rootDirectory = $root_directory; + return $this; + } + + public function getRootDirectory() { + return $this->rootDirectory; + } + + public function setType($type) { + $this->type = $type; + return $this; + } + + public function getType() { + return $this->type; + } + + public function setSkinClass($skin_class) { + $this->skinClass = $skin_class; + return $this; + } + + public function getSkinClass() { + return $this->skinClass; + } + + public function addPhutilLibrary($library) { + $this->phutilLibraries[] = $library; + return $this; + } + + public function buildSkin(AphrontRequest $request) { + foreach ($this->phutilLibraries as $library) { + phutil_load_library($library); + } + + return newv($this->getSkinClass(), array($request, $this)); + } + +} diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 318a0aa480..1d3c4d7693 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -24,7 +24,7 @@ final class PhameBlog extends PhameDAO const MARKUP_FIELD_DESCRIPTION = 'markup:description'; - const SKIN_DEFAULT = 'PhameBlogSkinTenEleven'; + const SKIN_DEFAULT = 'oblivious'; protected $id; protected $phid; @@ -57,14 +57,17 @@ final class PhameBlog extends PhameDAO } public function getSkinRenderer(AphrontRequest $request) { - try { - $skin = newv($this->getSkin(), array($request)); - } catch (PhutilMissingSymbolException $ex) { - // If this blog has a skin but it's no longer available (for example, - // it was uninstalled) just return the default skin. - $skin = newv(self::SKIN_DEFAULT, array($request)); + $spec = PhameSkinSpecification::loadOneSkinSpecification( + $this->getSkin()); + + if (!$spec) { + $spec = PhameSkinSpecification::loadOneSkinSpecification( + self::SKIN_DEFAULT); } + $skin = newv($spec->getSkinClass(), array($request)); + $skin->setSpecification($spec); + return $skin; } diff --git a/support/phame/libskin.php b/support/phame/libskin.php new file mode 100644 index 0000000000..70c33d1a7f --- /dev/null +++ b/support/phame/libskin.php @@ -0,0 +1,21 @@ +