@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

Remove skins from Phame

Summary:
Ref T9897. Purge a bunch of stuff:

- Remove skins.
- Remove all custom sites for skin resources.
- Remove "framed", "notlive", "preview", separate "live" controllers (see below).
- Merge "publish" and "unpublish" controllers into one.

New behavior:

- Blogs and posts have three views:
- "View": Internal view URI, which is a normal detail page.
- "Internal Live": Internal view URI which is a little prettier.
- "External Live": External view URI for an external domain.

Right now, the differences are pretty minor (basically, different crumbs/chrome). This mostly gives us room to put some milder flavor of skins back later (photography or more "presentation" elements, for example).

This removes 9 million lines of code so I probably missed a couple of things, but I think it's like 95% of the way there.

Test Plan:
Here are some examples of what the "view", "internal" and "external" views look like for blogs (posts are similar):

"View": Unchanged

{F1021634}

"Internal": No chrome or footer. Still write actions (edit, post commments). Has crumbs to get back into Phame.

{F1021635}

"External": No chrome or footer. No write actions. No Phabricator crumbs. No policy/status information.

{F1021638}

I figure we'll probably tweak these a bit to figure out what makes sense (like: maybe no actions on "internal, live"? and "external, live" probably needs a way to set a root "Company >" crumb?) but that they're reasonable-ish as a first cut?

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9897

Differential Revision: https://secure.phabricator.com/D14740

+449 -1730
-1
externals/skins/oblivious/404.php
··· 1 - <h2>404 Not Found</h2>
-76
externals/skins/oblivious/css/oblivious.css
··· 1 - html, body, p, h1, h2, h3 { 2 - padding: 0; 3 - margin: 0; 4 - font-weight: normal; 5 - } 6 - 7 - html { 8 - font-family: "Helvetica Neue", "Arial", sans-serif; 9 - font-size: 16px; 10 - overflow-y: scroll; 11 - color: #555555; 12 - } 13 - 14 - .oblivious-info { 15 - position: fixed; 16 - width: 15%; 17 - border-right: 1px solid #dfdfdf; 18 - top: 0; 19 - bottom: 0; 20 - left: 0; 21 - padding: 140px 2% 0; 22 - overflow: hidden; 23 - 24 - background: url(/image/badge.png); 25 - background-repeat: no-repeat; 26 - background-position: 20px 20px; 27 - } 28 - 29 - .oblivious-content { 30 - padding-top: 3%; 31 - margin-left: 22%; 32 - max-width: 800px; 33 - } 34 - 35 - a { 36 - color: #2980b9; 37 - text-decoration: none; 38 - } 39 - 40 - a:hover { 41 - text-decoration: underline; 42 - } 43 - 44 - h1 { 45 - font-size: 24px; 46 - font-weight: normal; 47 - } 48 - 49 - h2 { 50 - font-size: 22px; 51 - font-weight: bold; 52 - margin-bottom: 8px; 53 - } 54 - 55 - .phame-post { 56 - margin: 0 0 2em; 57 - } 58 - 59 - .phame-post-title { 60 - font-size: 28px; 61 - } 62 - 63 - .phame-post-date { 64 - font-size: 12px; 65 - margin: .25em 0 2em; 66 - } 67 - 68 - .oblivious-content .phabricator-remarkup ul.remarkup-list { 69 - margin-left: 0; 70 - } 71 - 72 - .fb-comments, 73 - .fb-comments span, 74 - .fb-comments iframe[style] { 75 - width: 100% !important; 76 - }
-3
externals/skins/oblivious/footer.php
··· 1 - </div> 2 - </body> 3 - </html>
-18
externals/skins/oblivious/header.php
··· 1 - <!DOCTYPE html> 2 - <html> 3 - <head> 4 - <title><?php echo _e($title); ?></title> 5 - 6 - <?php echo $skin->getCSSResources(); ?> 7 - 8 - </head> 9 - <body> 10 - <div class="oblivious-info"> 11 - <h1> 12 - <a href="<?php echo _e($home_uri); ?>"><?php 13 - echo _e($blog->getName()); 14 - ?></a> 15 - </h1> 16 - <p><?php echo $skin->remarkup($blog->getDescription()); ?></p> 17 - </div> 18 - <div class="oblivious-content">
externals/skins/oblivious/image/badge.png

This is a binary file and will not be displayed.

-1
externals/skins/oblivious/post-detail.php
··· 1 - <?php echo $post->render(); ?>
-13
externals/skins/oblivious/post-list.php
··· 1 - <div class="oblivious-post-list"> 2 - <?php 3 - 4 - foreach ($posts as $post) { 5 - echo $post->renderWithSummary(); 6 - } 7 - 8 - ?> 9 - </div> 10 - <div class="oblivious-pager"> 11 - <?php echo $older; ?> 12 - <?php echo $newer; ?> 13 - </div>
-3
externals/skins/oblivious/skin.json
··· 1 - { 2 - "name": "Oblivious" 3 - }
+4 -28
src/__phutil_library_map__.php
··· 2928 2928 'PhabricatorRepositoryVCSPassword' => 'applications/repository/storage/PhabricatorRepositoryVCSPassword.php', 2929 2929 'PhabricatorRepositoryVersion' => 'applications/repository/constants/PhabricatorRepositoryVersion.php', 2930 2930 'PhabricatorRequestExceptionHandler' => 'aphront/handler/PhabricatorRequestExceptionHandler.php', 2931 - 'PhabricatorResourceSite' => 'aphront/site/PhabricatorResourceSite.php', 2932 2931 'PhabricatorRobotsController' => 'applications/system/controller/PhabricatorRobotsController.php', 2933 2932 'PhabricatorS3FileStorageEngine' => 'applications/files/engine/PhabricatorS3FileStorageEngine.php', 2934 2933 'PhabricatorSMS' => 'infrastructure/sms/storage/PhabricatorSMS.php', ··· 3308 3307 'PhabricatorXHProfSample' => 'applications/xhprof/storage/PhabricatorXHProfSample.php', 3309 3308 'PhabricatorXHProfSampleListController' => 'applications/xhprof/controller/PhabricatorXHProfSampleListController.php', 3310 3309 'PhabricatorYoutubeRemarkupRule' => 'infrastructure/markup/rule/PhabricatorYoutubeRemarkupRule.php', 3311 - 'PhameBasicBlogSkin' => 'applications/phame/skins/PhameBasicBlogSkin.php', 3312 - 'PhameBasicTemplateBlogSkin' => 'applications/phame/skins/PhameBasicTemplateBlogSkin.php', 3313 3310 'PhameBlog' => 'applications/phame/storage/PhameBlog.php', 3314 3311 'PhameBlogArchiveController' => 'applications/phame/controller/blog/PhameBlogArchiveController.php', 3315 3312 'PhameBlogController' => 'applications/phame/controller/blog/PhameBlogController.php', ··· 3318 3315 'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php', 3319 3316 'PhameBlogFeedController' => 'applications/phame/controller/blog/PhameBlogFeedController.php', 3320 3317 'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php', 3321 - 'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php', 3322 3318 'PhameBlogManageController' => 'applications/phame/controller/blog/PhameBlogManageController.php', 3323 3319 'PhameBlogProfilePictureController' => 'applications/phame/controller/blog/PhameBlogProfilePictureController.php', 3324 3320 'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php', 3325 3321 'PhameBlogReplyHandler' => 'applications/phame/mail/PhameBlogReplyHandler.php', 3326 3322 'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php', 3327 3323 'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php', 3328 - 'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php', 3329 3324 'PhameBlogTransaction' => 'applications/phame/storage/PhameBlogTransaction.php', 3330 3325 'PhameBlogTransactionQuery' => 'applications/phame/query/PhameBlogTransactionQuery.php', 3331 3326 'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php', 3332 - 'PhameCelerityResources' => 'applications/phame/celerity/PhameCelerityResources.php', 3333 3327 'PhameConduitAPIMethod' => 'applications/phame/conduit/PhameConduitAPIMethod.php', 3334 3328 'PhameConstants' => 'applications/phame/constants/PhameConstants.php', 3335 3329 'PhameController' => 'applications/phame/controller/PhameController.php', ··· 3337 3331 'PhameDAO' => 'applications/phame/storage/PhameDAO.php', 3338 3332 'PhameDescriptionView' => 'applications/phame/view/PhameDescriptionView.php', 3339 3333 'PhameHomeController' => 'applications/phame/controller/PhameHomeController.php', 3334 + 'PhameLiveController' => 'applications/phame/controller/PhameLiveController.php', 3340 3335 'PhamePost' => 'applications/phame/storage/PhamePost.php', 3341 3336 'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php', 3342 3337 'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php', 3343 3338 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 3344 3339 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', 3345 - 'PhamePostFramedController' => 'applications/phame/controller/post/PhamePostFramedController.php', 3346 3340 'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php', 3347 3341 'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php', 3348 3342 'PhamePostListView' => 'applications/phame/view/PhamePostListView.php', 3349 3343 'PhamePostMailReceiver' => 'applications/phame/mail/PhamePostMailReceiver.php', 3350 3344 'PhamePostMoveController' => 'applications/phame/controller/post/PhamePostMoveController.php', 3351 3345 'PhamePostNewController' => 'applications/phame/controller/post/PhamePostNewController.php', 3352 - 'PhamePostNotLiveController' => 'applications/phame/controller/post/PhamePostNotLiveController.php', 3353 - 'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php', 3354 3346 'PhamePostPublishController' => 'applications/phame/controller/post/PhamePostPublishController.php', 3355 3347 'PhamePostQuery' => 'applications/phame/query/PhamePostQuery.php', 3356 3348 'PhamePostReplyHandler' => 'applications/phame/mail/PhamePostReplyHandler.php', ··· 3358 3350 'PhamePostTransaction' => 'applications/phame/storage/PhamePostTransaction.php', 3359 3351 'PhamePostTransactionComment' => 'applications/phame/storage/PhamePostTransactionComment.php', 3360 3352 'PhamePostTransactionQuery' => 'applications/phame/query/PhamePostTransactionQuery.php', 3361 - 'PhamePostUnpublishController' => 'applications/phame/controller/post/PhamePostUnpublishController.php', 3362 - 'PhamePostView' => 'applications/phame/view/PhamePostView.php', 3363 3353 'PhamePostViewController' => 'applications/phame/controller/post/PhamePostViewController.php', 3364 3354 'PhameQueryConduitAPIMethod' => 'applications/phame/conduit/PhameQueryConduitAPIMethod.php', 3365 3355 'PhameQueryPostsConduitAPIMethod' => 'applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php', 3366 - 'PhameResourceController' => 'applications/phame/controller/PhameResourceController.php', 3367 3356 'PhameSchemaSpec' => 'applications/phame/storage/PhameSchemaSpec.php', 3368 3357 'PhameSite' => 'applications/phame/site/PhameSite.php', 3369 - 'PhameSkinSpecification' => 'applications/phame/skins/PhameSkinSpecification.php', 3370 3358 'PhluxController' => 'applications/phlux/controller/PhluxController.php', 3371 3359 'PhluxDAO' => 'applications/phlux/storage/PhluxDAO.php', 3372 3360 'PhluxEditController' => 'applications/phlux/controller/PhluxEditController.php', ··· 7222 7210 'PhabricatorRepositoryVCSPassword' => 'PhabricatorRepositoryDAO', 7223 7211 'PhabricatorRepositoryVersion' => 'Phobject', 7224 7212 'PhabricatorRequestExceptionHandler' => 'AphrontRequestExceptionHandler', 7225 - 'PhabricatorResourceSite' => 'PhabricatorSite', 7226 7213 'PhabricatorRobotsController' => 'PhabricatorController', 7227 7214 'PhabricatorS3FileStorageEngine' => 'PhabricatorFileStorageEngine', 7228 7215 'PhabricatorSMS' => 'PhabricatorSMSDAO', ··· 7650 7637 'PhabricatorXHProfSample' => 'PhabricatorXHProfDAO', 7651 7638 'PhabricatorXHProfSampleListController' => 'PhabricatorXHProfController', 7652 7639 'PhabricatorYoutubeRemarkupRule' => 'PhutilRemarkupRule', 7653 - 'PhameBasicBlogSkin' => 'PhameBlogSkin', 7654 - 'PhameBasicTemplateBlogSkin' => 'PhameBasicBlogSkin', 7655 7640 'PhameBlog' => array( 7656 7641 'PhameDAO', 7657 7642 'PhabricatorPolicyInterface', ··· 7669 7654 'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor', 7670 7655 'PhameBlogFeedController' => 'PhameBlogController', 7671 7656 'PhameBlogListController' => 'PhameBlogController', 7672 - 'PhameBlogLiveController' => 'PhameBlogController', 7673 7657 'PhameBlogManageController' => 'PhameBlogController', 7674 7658 'PhameBlogProfilePictureController' => 'PhameBlogController', 7675 7659 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7676 7660 'PhameBlogReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 7677 7661 'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine', 7678 7662 'PhameBlogSite' => 'PhameSite', 7679 - 'PhameBlogSkin' => 'PhabricatorController', 7680 7663 'PhameBlogTransaction' => 'PhabricatorApplicationTransaction', 7681 7664 'PhameBlogTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 7682 - 'PhameBlogViewController' => 'PhameBlogController', 7683 - 'PhameCelerityResources' => 'CelerityResources', 7665 + 'PhameBlogViewController' => 'PhameLiveController', 7684 7666 'PhameConduitAPIMethod' => 'ConduitAPIMethod', 7685 7667 'PhameConstants' => 'Phobject', 7686 7668 'PhameController' => 'PhabricatorController', ··· 7688 7670 'PhameDAO' => 'PhabricatorLiskDAO', 7689 7671 'PhameDescriptionView' => 'AphrontTagView', 7690 7672 'PhameHomeController' => 'PhamePostController', 7673 + 'PhameLiveController' => 'PhameController', 7691 7674 'PhamePost' => array( 7692 7675 'PhameDAO', 7693 7676 'PhabricatorPolicyInterface', ··· 7703 7686 'PhamePostController' => 'PhameController', 7704 7687 'PhamePostEditController' => 'PhamePostController', 7705 7688 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor', 7706 - 'PhamePostFramedController' => 'PhamePostController', 7707 7689 'PhamePostHistoryController' => 'PhamePostController', 7708 7690 'PhamePostListController' => 'PhamePostController', 7709 7691 'PhamePostListView' => 'AphrontTagView', 7710 7692 'PhamePostMailReceiver' => 'PhabricatorObjectMailReceiver', 7711 7693 'PhamePostMoveController' => 'PhamePostController', 7712 7694 'PhamePostNewController' => 'PhamePostController', 7713 - 'PhamePostNotLiveController' => 'PhamePostController', 7714 - 'PhamePostPreviewController' => 'PhamePostController', 7715 7695 'PhamePostPublishController' => 'PhamePostController', 7716 7696 'PhamePostQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 7717 7697 'PhamePostReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', ··· 7719 7699 'PhamePostTransaction' => 'PhabricatorApplicationTransaction', 7720 7700 'PhamePostTransactionComment' => 'PhabricatorApplicationTransactionComment', 7721 7701 'PhamePostTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 7722 - 'PhamePostUnpublishController' => 'PhamePostController', 7723 - 'PhamePostView' => 'AphrontView', 7724 - 'PhamePostViewController' => 'PhamePostController', 7702 + 'PhamePostViewController' => 'PhameLiveController', 7725 7703 'PhameQueryConduitAPIMethod' => 'PhameConduitAPIMethod', 7726 7704 'PhameQueryPostsConduitAPIMethod' => 'PhameConduitAPIMethod', 7727 - 'PhameResourceController' => 'CelerityResourceController', 7728 7705 'PhameSchemaSpec' => 'PhabricatorConfigSchemaSpec', 7729 7706 'PhameSite' => 'PhabricatorSite', 7730 - 'PhameSkinSpecification' => 'Phobject', 7731 7707 'PhluxController' => 'PhabricatorController', 7732 7708 'PhluxDAO' => 'PhabricatorLiskDAO', 7733 7709 'PhluxEditController' => 'PhluxController',
-41
src/aphront/site/PhabricatorResourceSite.php
··· 1 - <?php 2 - 3 - final class PhabricatorResourceSite extends PhabricatorSite { 4 - 5 - public function getDescription() { 6 - return pht('Serves static resources like images, CSS and JS.'); 7 - } 8 - 9 - public function getPriority() { 10 - return 2000; 11 - } 12 - 13 - public function newSiteForRequest(AphrontRequest $request) { 14 - $host = $request->getHost(); 15 - 16 - $uri = PhabricatorEnv::getEnvConfig('security.alternate-file-domain'); 17 - if (!strlen($uri)) { 18 - return null; 19 - } 20 - 21 - if ($this->isHostMatch($host, array($uri))) { 22 - return new PhabricatorResourceSite(); 23 - } 24 - 25 - return null; 26 - } 27 - 28 - public function getRoutingMaps() { 29 - $applications = PhabricatorApplication::getAllInstalledApplications(); 30 - 31 - $maps = array(); 32 - foreach ($applications as $application) { 33 - $maps[] = $this->newRoutingMap() 34 - ->setApplication($application) 35 - ->setRoutes($application->getResourceRoutes()); 36 - } 37 - 38 - return $maps; 39 - } 40 - 41 - }
+13 -16
src/applications/phame/application/PhabricatorPhameApplication.php
··· 39 39 return array( 40 40 '/phame/' => array( 41 41 '' => 'PhameHomeController', 42 - 'live/(?P<id>[^/]+)/(?P<more>.*)' => 'PhameBlogLiveController', 42 + 43 + // NOTE: The live routes include an initial "/", so leave it off 44 + // this route. 45 + '(?P<live>live)/(?P<blogID>[^/]+)' => $this->getLiveRoutes(), 43 46 'post/' => array( 44 - '(?:(?P<filter>draft|all)/)?' => 'PhamePostListController', 45 47 '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhamePostListController', 46 48 'blogger/(?P<bloggername>[\w\.-_]+)/' => 'PhamePostListController', 47 49 'edit/(?:(?P<id>[^/]+)/)?' => 'PhamePostEditController', 48 50 'history/(?P<id>\d+)/' => 'PhamePostHistoryController', 49 - 'view/(?P<id>\d+)/' => 'PhamePostViewController', 50 - 'view/(?P<id>\d+)/(?P<slug>[^/]+)/' => 'PhamePostViewController', 51 - 'publish/(?P<id>\d+)/' => 'PhamePostPublishController', 51 + 'view/(?P<id>\d+)/(?:(?P<slug>[^/]+)/)?' => 'PhamePostViewController', 52 + '(?P<action>publish|unpublish)/(?P<id>\d+)/' 53 + => 'PhamePostPublishController', 52 54 'preview/(?P<id>\d+)/' => 'PhamePostPreviewController', 53 - 'unpublish/(?P<id>\d+)/' => 'PhamePostUnpublishController', 54 - 'notlive/(?P<id>\d+)/' => 'PhamePostNotLiveController', 55 55 'preview/' => 'PhabricatorMarkupPreviewController', 56 56 'framed/(?P<id>\d+)/' => 'PhamePostFramedController', 57 57 'new/' => 'PhamePostNewController', ··· 59 59 'comment/(?P<id>[1-9]\d*)/' => 'PhamePostCommentController', 60 60 ), 61 61 'blog/' => array( 62 - '(?:(?P<filter>user|all)/)?' => 'PhameBlogListController', 63 62 '(?:query/(?P<queryKey>[^/]+)/)?' => 'PhameBlogListController', 64 63 'archive/(?P<id>[^/]+)/' => 'PhameBlogArchiveController', 65 64 'edit/(?P<id>[^/]+)/' => 'PhameBlogEditController', 66 - 'view/(?P<id>[^/]+)/' => 'PhameBlogViewController', 65 + 'view/(?P<blogID>[^/]+)/' => 'PhameBlogViewController', 67 66 'manage/(?P<id>[^/]+)/' => 'PhameBlogManageController', 68 67 'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController', 69 68 'new/' => 'PhameBlogEditController', ··· 87 86 } 88 87 89 88 public function getBlogRoutes() { 90 - return array( 91 - '/(?P<more>.*)' => 'PhameBlogLiveController', 92 - ); 89 + return $this->getLiveRoutes(); 93 90 } 94 91 95 - public function getBlogCDNRoutes() { 92 + private function getLiveRoutes() { 96 93 return array( 97 - '/phame/' => array( 98 - 'r/(?P<id>\d+)/(?P<hash>[^/]+)/(?P<name>.*)' => 99 - 'PhameResourceController', 94 + '/' => array( 95 + '' => 'PhameBlogViewController', 96 + 'post/(?P<id>[^/]+)/(?:(?P<slug>[^/]+)/)?' => 'PhamePostViewController', 100 97 ), 101 98 ); 102 99 }
-28
src/applications/phame/celerity/PhameCelerityResources.php
··· 1 - <?php 2 - 3 - /** 4 - * Defines Phabricator's static resources. 5 - */ 6 - final class PhameCelerityResources extends CelerityResources { 7 - 8 - private $skin; 9 - 10 - public function setSkin($skin) { 11 - $this->skin = $skin; 12 - return $this; 13 - } 14 - 15 - public function getSkin() { 16 - return $this->skin; 17 - } 18 - 19 - public function getName() { 20 - return 'phame:'.$this->getSkin()->getName(); 21 - } 22 - 23 - public function getResourceData($name) { 24 - $resource_path = $this->skin->getRootDirectory().DIRECTORY_SEPARATOR.$name; 25 - return Filesystem::readFile($resource_path); 26 - } 27 - 28 - }
+182
src/applications/phame/controller/PhameLiveController.php
··· 1 + <?php 2 + 3 + abstract class PhameLiveController extends PhameController { 4 + 5 + private $isExternal; 6 + private $isLive; 7 + private $blog; 8 + private $post; 9 + 10 + public function shouldAllowPublic() { 11 + return true; 12 + } 13 + 14 + protected function getIsExternal() { 15 + return $this->isExternal; 16 + } 17 + 18 + protected function getIsLive() { 19 + return $this->isLive; 20 + } 21 + 22 + protected function getBlog() { 23 + return $this->blog; 24 + } 25 + 26 + protected function getPost() { 27 + return $this->post; 28 + } 29 + 30 + protected function setupLiveEnvironment() { 31 + $request = $this->getRequest(); 32 + $viewer = $this->getViewer(); 33 + 34 + $site = $request->getSite(); 35 + $blog_id = $request->getURIData('blogID'); 36 + $post_id = $request->getURIData('id'); 37 + 38 + if ($site instanceof PhameBlogSite) { 39 + // This is a live page on a custom domain. We already looked up the blog 40 + // in the Site handler by examining the domain, so we don't need to do 41 + // more lookups. 42 + 43 + $blog = $site->getBlog(); 44 + $is_external = true; 45 + $is_live = true; 46 + } else if ($blog_id) { 47 + // This is a blog detail view, an internal blog live view, or an 48 + // internal post live view The internal post detail view is handled 49 + // below. 50 + 51 + $is_external = false; 52 + if ($request->getURIData('live')) { 53 + $is_live = true; 54 + } else { 55 + $is_live = false; 56 + } 57 + 58 + $blog_query = id(new PhameBlogQuery()) 59 + ->setViewer($viewer) 60 + ->needProfileImage(true) 61 + ->withIDs(array($blog_id)); 62 + 63 + // If this is a live view, only show active blogs. 64 + if ($is_live) { 65 + $blog_query->withStatuses( 66 + array( 67 + PhameBlog::STATUS_ACTIVE, 68 + )); 69 + } 70 + 71 + $blog = $blog_query->executeOne(); 72 + if (!$blog) { 73 + return new Aphront404Response(); 74 + } 75 + 76 + } else { 77 + // This is a post detail page, so we'll figure out the blog by loading 78 + // the post first. 79 + $is_external = false; 80 + $is_live = false; 81 + $blog = null; 82 + } 83 + 84 + if ($post_id) { 85 + $post_query = id(new PhamePostQuery()) 86 + ->setViewer($viewer) 87 + ->withIDs(array($post_id)); 88 + 89 + if ($blog) { 90 + $post_query->withBlogPHIDs(array($blog->getPHID())); 91 + } 92 + 93 + // Only show published posts on external domains. 94 + if ($is_external) { 95 + $post_query->withVisibility(PhameConstants::VISIBILITY_PUBLISHED); 96 + } 97 + 98 + $post = $post_query->executeOne(); 99 + if (!$post) { 100 + return new Aphront404Response(); 101 + } 102 + 103 + // If this is a post detail page, the URI didn't come with a blog ID, 104 + // so fill that in. 105 + if (!$blog) { 106 + $blog = $post->getBlog(); 107 + } 108 + } else { 109 + $post = null; 110 + } 111 + 112 + $this->isExternal = $is_external; 113 + $this->isLive = $is_live; 114 + $this->blog = $blog; 115 + $this->post = $post; 116 + 117 + // If we have a post, canonicalize the URI to the post's current slug and 118 + // redirect the user if it isn't correct. 119 + if ($post) { 120 + $slug = $request->getURIData('slug'); 121 + if ($post->getSlug() != $slug) { 122 + if ($is_live) { 123 + if ($is_external) { 124 + $uri = $post->getExternalLiveURI(); 125 + } else { 126 + $uri = $post->getInternalLiveURI(); 127 + } 128 + } else { 129 + $uri = $post->getViewURI(); 130 + } 131 + 132 + return id(new AphrontRedirectResponse())->setURI($uri); 133 + } 134 + } 135 + 136 + return null; 137 + } 138 + 139 + protected function buildApplicationCrumbs() { 140 + $blog = $this->getBlog(); 141 + $post = $this->getPost(); 142 + 143 + $is_live = $this->getIsLive(); 144 + $is_external = $this->getIsExternal(); 145 + 146 + // If this is an external view, don't put the "Phame" crumb or the 147 + // "Blogs" crumb into the crumbs list. 148 + if ($is_external) { 149 + $crumbs = new PHUICrumbsView(); 150 + } else { 151 + $crumbs = parent::buildApplicationCrumbs(); 152 + $crumbs->addTextCrumb( 153 + pht('Blogs'), 154 + $this->getApplicationURI('blog/')); 155 + } 156 + 157 + $crumbs->setBorder(true); 158 + 159 + if ($post) { 160 + if ($is_live) { 161 + if ($is_external) { 162 + $blog_uri = $blog->getExternalLiveURI(); 163 + } else { 164 + $blog_uri = $blog->getInternalLiveURI(); 165 + } 166 + } else { 167 + $blog_uri = $blog->getViewURI(); 168 + } 169 + } else { 170 + $blog_uri = null; 171 + } 172 + 173 + $crumbs->addTextCrumb($blog->getName(), $blog_uri); 174 + 175 + if ($post) { 176 + $crumbs->addTextCrumb($post->getTitle()); 177 + } 178 + 179 + return $crumbs; 180 + } 181 + 182 + }
-72
src/applications/phame/controller/PhameResourceController.php
··· 1 - <?php 2 - 3 - final class PhameResourceController extends CelerityResourceController { 4 - 5 - private $id; 6 - private $hash; 7 - private $name; 8 - private $root; 9 - private $celerityResourceMap; 10 - 11 - public function getCelerityResourceMap() { 12 - return $this->celerityResourceMap; 13 - } 14 - 15 - public function willProcessRequest(array $data) { 16 - $this->id = $data['id']; 17 - $this->hash = $data['hash']; 18 - $this->name = $data['name']; 19 - } 20 - 21 - public function processRequest() { 22 - $request = $this->getRequest(); 23 - $user = $request->getUser(); 24 - 25 - // We require a visible blog associated with a given skin to serve 26 - // resources, so you can't go fishing around where you shouldn't be. 27 - // However, since these resources may be served off a CDN domain, we're 28 - // bypassing the actual policy check. The blog needs to exist, but you 29 - // don't necessarily need to be able to see it in order to see static 30 - // resources on it. 31 - 32 - $blog = id(new PhameBlogQuery()) 33 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 34 - ->withIDs(array($this->id)) 35 - ->executeOne(); 36 - if (!$blog) { 37 - return new Aphront404Response(); 38 - } 39 - 40 - $skin = $blog->getSkinRenderer($request); 41 - $spec = $skin->getSpecification(); 42 - 43 - $resources = new PhameCelerityResources(); 44 - $resources->setSkin($spec); 45 - 46 - $this->root = $spec->getRootDirectory(); 47 - $this->celerityResourceMap = new CelerityResourceMap($resources); 48 - 49 - return $this->serveResource($this->name); 50 - } 51 - 52 - protected function buildResourceTransformer() { 53 - $xformer = new CelerityResourceTransformer(); 54 - $xformer->setMinify(false); 55 - $xformer->setTranslateURICallback(array($this, 'translateResourceURI')); 56 - return $xformer; 57 - } 58 - 59 - public function translateResourceURI(array $matches) { 60 - $uri = trim($matches[1], "'\" \r\t\n"); 61 - 62 - if (Filesystem::pathExists($this->root.$uri)) { 63 - $hash = filemtime($this->root.$uri); 64 - } else { 65 - $hash = '-'; 66 - } 67 - 68 - $uri = '/phame/r/'.$this->id.'/'.$hash.'/'.$uri; 69 - return 'url('.$uri.')'; 70 - } 71 - 72 - }
-14
src/applications/phame/controller/blog/PhameBlogEditController.php
··· 46 46 $name = $blog->getName(); 47 47 $description = $blog->getDescription(); 48 48 $custom_domain = $blog->getDomain(); 49 - $skin = $blog->getSkin(); 50 49 $can_view = $blog->getViewPolicy(); 51 50 $can_edit = $blog->getEditPolicy(); 52 51 ··· 58 57 $name = $request->getStr('name'); 59 58 $description = $request->getStr('description'); 60 59 $custom_domain = nonempty($request->getStr('custom_domain'), null); 61 - $skin = $request->getStr('skin'); 62 60 $can_view = $request->getStr('can_view'); 63 61 $can_edit = $request->getStr('can_edit'); 64 62 $v_projects = $request->getArr('projects'); ··· 74 72 id(new PhameBlogTransaction()) 75 73 ->setTransactionType(PhameBlogTransaction::TYPE_DOMAIN) 76 74 ->setNewValue($custom_domain), 77 - id(new PhameBlogTransaction()) 78 - ->setTransactionType(PhameBlogTransaction::TYPE_SKIN) 79 - ->setNewValue($skin), 80 75 id(new PhameBlogTransaction()) 81 76 ->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY) 82 77 ->setNewValue($can_view), ··· 120 115 ->setObject($blog) 121 116 ->execute(); 122 117 123 - $skins = PhameSkinSpecification::loadAllSkinSpecifications(); 124 - $skins = mpull($skins, 'getName'); 125 - 126 118 $form = id(new AphrontFormView()) 127 119 ->setUser($viewer) 128 120 ->appendChild( ··· 179 171 ->setCaption( 180 172 pht('Must include at least one dot (.), e.g. %s', 'blog.example.com')) 181 173 ->setError($e_custom_domain)) 182 - ->appendChild( 183 - id(new AphrontFormSelectControl()) 184 - ->setLabel(pht('Skin')) 185 - ->setName('skin') 186 - ->setValue($skin) 187 - ->setOptions($skins)) 188 174 ->appendChild( 189 175 id(new AphrontFormSubmitControl()) 190 176 ->addCancelButton($cancel_uri)
-70
src/applications/phame/controller/blog/PhameBlogLiveController.php
··· 1 - <?php 2 - 3 - final class PhameBlogLiveController extends PhameBlogController { 4 - 5 - public function shouldAllowPublic() { 6 - return true; 7 - } 8 - 9 - public function handleRequest(AphrontRequest $request) { 10 - $viewer = $request->getViewer(); 11 - 12 - $site = $request->getSite(); 13 - if ($site instanceof PhameBlogSite) { 14 - $blog = $site->getBlog(); 15 - } else { 16 - $id = $request->getURIData('id'); 17 - 18 - $blog = id(new PhameBlogQuery()) 19 - ->setViewer($viewer) 20 - ->withIDs(array($id)) 21 - ->executeOne(); 22 - if (!$blog) { 23 - return new Aphront404Response(); 24 - } 25 - } 26 - 27 - if ($blog->getDomain() && ($request->getHost() != $blog->getDomain())) { 28 - $base_uri = $blog->getLiveURI(); 29 - 30 - // Don't redirect directly, since the domain is user-controlled and there 31 - // are a bevy of security issues associated with automatic redirects to 32 - // external domains. 33 - 34 - // Previously we CSRF'd this and someone found a way to pass OAuth 35 - // information through it using anchors. Just make users click a normal 36 - // link so that this is no more dangerous than any other external link 37 - // on the site. 38 - 39 - $dialog = id(new AphrontDialogView()) 40 - ->setTitle(pht('Blog Moved')) 41 - ->setUser($viewer) 42 - ->appendParagraph(pht('This blog is now hosted here:')) 43 - ->appendParagraph( 44 - phutil_tag( 45 - 'a', 46 - array( 47 - 'href' => $base_uri, 48 - ), 49 - $base_uri)) 50 - ->addCancelButton('/'); 51 - 52 - return id(new AphrontDialogResponse())->setDialog($dialog); 53 - } 54 - 55 - $phame_request = clone $request; 56 - $more = $phame_request->getURIData('more', ''); 57 - $phame_request->setPath('/'.ltrim($more, '/')); 58 - 59 - $uri = $blog->getLiveURI(); 60 - 61 - $skin = $blog->getSkinRenderer($phame_request); 62 - $skin 63 - ->setBlog($blog) 64 - ->setBaseURI($uri); 65 - 66 - $skin->willProcessRequest(array()); 67 - return $skin->processRequest(); 68 - } 69 - 70 - }
-6
src/applications/phame/controller/blog/PhameBlogManageController.php
··· 80 80 ->setObject($blog) 81 81 ->setActionList($actions); 82 82 83 - $skin = $blog->getSkin(); 84 - if (!$skin) { 85 - $skin = phutil_tag('em', array(), pht('No external skin')); 86 - } 87 - 88 83 $domain = $blog->getDomain(); 89 84 if (!$domain) { 90 85 $domain = phutil_tag('em', array(), pht('No external domain')); 91 86 } 92 87 93 - $properties->addProperty(pht('Skin'), $skin); 94 88 $properties->addProperty(pht('Domain'), $domain); 95 89 96 90 $feed_uri = PhabricatorEnv::getProductionURI(
+62 -55
src/applications/phame/controller/blog/PhameBlogViewController.php
··· 1 1 <?php 2 2 3 - final class PhameBlogViewController extends PhameBlogController { 4 - 5 - private $blog; 6 - 7 - public function shouldAllowPublic() { 8 - return true; 9 - } 3 + final class PhameBlogViewController extends PhameLiveController { 10 4 11 5 public function handleRequest(AphrontRequest $request) { 12 - $viewer = $request->getViewer(); 13 - $id = $request->getURIData('id'); 14 - 15 - $blog = id(new PhameBlogQuery()) 16 - ->setViewer($viewer) 17 - ->withIDs(array($id)) 18 - ->needProfileImage(true) 19 - ->executeOne(); 20 - if (!$blog) { 21 - return new Aphront404Response(); 6 + $response = $this->setupLiveEnvironment(); 7 + if ($response) { 8 + return $response; 22 9 } 23 - $this->blog = $blog; 10 + 11 + $viewer = $this->getViewer(); 12 + $blog = $this->getBlog(); 13 + 14 + $is_live = $this->getIsLive(); 15 + $is_external = $this->getIsExternal(); 24 16 25 17 $pager = id(new AphrontCursorPagerView()) 26 18 ->readFromRequest($request); 27 19 28 - $posts = id(new PhamePostQuery()) 20 + $post_query = id(new PhamePostQuery()) 29 21 ->setViewer($viewer) 30 - ->withBlogPHIDs(array($blog->getPHID())) 31 - ->executeWithCursorPager($pager); 22 + ->withBlogPHIDs(array($blog->getPHID())); 32 23 33 - if ($blog->isArchived()) { 34 - $header_icon = 'fa-ban'; 35 - $header_name = pht('Archived'); 36 - $header_color = 'dark'; 37 - } else { 38 - $header_icon = 'fa-check'; 39 - $header_name = pht('Active'); 40 - $header_color = 'bluegrey'; 24 + if ($is_live) { 25 + $post_query->withVisibility(PhameConstants::VISIBILITY_PUBLISHED); 41 26 } 42 27 43 - $actions = $this->renderActions($blog, $viewer); 44 - $action_button = id(new PHUIButtonView()) 45 - ->setTag('a') 46 - ->setText(pht('Actions')) 47 - ->setHref('#') 48 - ->setIconFont('fa-bars') 49 - ->addClass('phui-mobile-menu') 50 - ->setDropdownMenu($actions); 28 + $posts = $post_query->executeWithCursorPager($pager); 51 29 52 30 $header = id(new PHUIHeaderView()) 53 31 ->setHeader($blog->getName()) 54 - ->setUser($viewer) 55 - ->setPolicyObject($blog) 56 - ->setStatus($header_icon, $header_color, $header_name) 57 - ->addActionLink($action_button); 32 + ->setUser($viewer); 33 + 34 + if (!$is_external) { 35 + if ($blog->isArchived()) { 36 + $header_icon = 'fa-ban'; 37 + $header_name = pht('Archived'); 38 + $header_color = 'dark'; 39 + } else { 40 + $header_icon = 'fa-check'; 41 + $header_name = pht('Active'); 42 + $header_color = 'bluegrey'; 43 + } 44 + $header->setStatus($header_icon, $header_color, $header_name); 45 + 46 + $actions = $this->renderActions($blog); 47 + $action_button = id(new PHUIButtonView()) 48 + ->setTag('a') 49 + ->setText(pht('Actions')) 50 + ->setHref('#') 51 + ->setIconFont('fa-bars') 52 + ->addClass('phui-mobile-menu') 53 + ->setDropdownMenu($actions); 54 + 55 + $header->addActionLink($action_button); 56 + 57 + $header->setPolicyObject($blog); 58 + } 58 59 59 60 $post_list = id(new PhamePostListView()) 60 61 ->setPosts($posts) 61 62 ->setViewer($viewer) 63 + ->setIsExternal($is_external) 64 + ->setIsLive($is_live) 62 65 ->setNodata(pht('This blog has no visible posts.')); 63 66 64 - $crumbs = $this->buildApplicationCrumbs(); 65 - $crumbs->setBorder(true); 66 - $crumbs->addTextCrumb( 67 - pht('Blogs'), 68 - $this->getApplicationURI('blog/')); 69 - $crumbs->addTextCrumb( 70 - $blog->getName()); 71 - 72 67 $page = id(new PHUIDocumentViewPro()) 73 68 ->setHeader($header) 74 69 ->appendChild($post_list); 75 70 76 71 $description = null; 77 72 if (strlen($blog->getDescription())) { 78 - $description = PhabricatorMarkupEngine::renderOneObject( 79 - id(new PhabricatorMarkupOneOff())->setContent($blog->getDescription()), 80 - 'default', 81 - $viewer); 73 + $description = new PHUIRemarkupView( 74 + $viewer, 75 + $blog->getDescription()); 82 76 } else { 83 77 $description = phutil_tag('em', array(), pht('No description.')); 84 78 } ··· 88 82 ->setDescription($description) 89 83 ->setImage($blog->getProfileImageURI()); 90 84 91 - return $this->newPage() 85 + $crumbs = $this->buildApplicationCrumbs(); 86 + 87 + $page = $this->newPage() 92 88 ->setTitle($blog->getName()) 89 + ->setPageObjectPHIDs(array($blog->getPHID())) 93 90 ->setCrumbs($crumbs) 94 91 ->appendChild( 95 92 array( 96 93 $page, 97 94 $about, 98 95 )); 96 + 97 + if ($is_live) { 98 + $page 99 + ->setShowChrome(false) 100 + ->setShowFooter(false); 101 + } 102 + 103 + return $page; 99 104 } 100 105 101 - private function renderActions(PhameBlog $blog, PhabricatorUser $viewer) { 106 + private function renderActions(PhameBlog $blog) { 107 + $viewer = $this->getViewer(); 108 + 102 109 $actions = id(new PhabricatorActionListView()) 103 110 ->setObject($blog) 104 111 ->setObjectURI($this->getRequest()->getRequestURI())
-39
src/applications/phame/controller/post/PhamePostFramedController.php
··· 1 - <?php 2 - 3 - final class PhamePostFramedController extends PhamePostController { 4 - 5 - public function handleRequest(AphrontRequest $request) { 6 - $viewer = $request->getViewer(); 7 - $id = $request->getURIData('id'); 8 - 9 - $post = id(new PhamePostQuery()) 10 - ->setViewer($viewer) 11 - ->withIDs(array($id)) 12 - ->requireCapabilities( 13 - array( 14 - PhabricatorPolicyCapability::CAN_EDIT, 15 - )) 16 - ->executeOne(); 17 - if (!$post) { 18 - return new Aphront404Response(); 19 - } 20 - 21 - $blog = $post->getBlog(); 22 - 23 - $phame_request = $request->setPath('/post/'.$post->getPhameTitle()); 24 - $skin = $post->getBlog()->getSkinRenderer($phame_request); 25 - 26 - $uri = clone $request->getRequestURI(); 27 - $uri->setPath('/phame/live/'.$blog->getID().'/'); 28 - 29 - $skin 30 - ->setPreview(true) 31 - ->setBlog($post->getBlog()) 32 - ->setBaseURI((string)$uri); 33 - 34 - $response = $skin->processRequest(); 35 - $response->setFrameable(true); 36 - return $response; 37 - } 38 - 39 - }
-45
src/applications/phame/controller/post/PhamePostNotLiveController.php
··· 1 - <?php 2 - 3 - final class PhamePostNotLiveController extends PhamePostController { 4 - 5 - public function handleRequest(AphrontRequest $request) { 6 - $viewer = $request->getViewer(); 7 - $id = $request->getURIData('id'); 8 - 9 - $post = id(new PhamePostQuery()) 10 - ->setViewer($viewer) 11 - ->withIDs(array($id)) 12 - ->executeOne(); 13 - if (!$post) { 14 - return new Aphront404Response(); 15 - } 16 - 17 - $reasons = array(); 18 - if ($post->isDraft()) { 19 - $reasons[] = phutil_tag('p', array(), pht( 20 - 'You can not view the live version of this post because it '. 21 - 'is still a draft. Use "Preview" or "Publish" to publish the post.')); 22 - } 23 - 24 - if ($reasons) { 25 - $cancel_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/'); 26 - 27 - $dialog = id(new AphrontDialogView()) 28 - ->setUser($viewer) 29 - ->setTitle(pht('Post Not Live')) 30 - ->addCancelButton($cancel_uri); 31 - 32 - foreach ($reasons as $reason) { 33 - $dialog->appendChild($reason); 34 - } 35 - 36 - return id(new AphrontDialogResponse())->setDialog($dialog); 37 - } 38 - 39 - // No reason this can't go live, maybe an old link. Kick them live and see 40 - // what happens. 41 - $live_uri = $post->getLiveURI(); 42 - return id(new AphrontRedirectResponse())->setURI($live_uri); 43 - } 44 - 45 - }
-89
src/applications/phame/controller/post/PhamePostPreviewController.php
··· 1 - <?php 2 - 3 - final class PhamePostPreviewController extends PhamePostController { 4 - 5 - public function handleRequest(AphrontRequest $request) { 6 - $viewer = $request->getViewer(); 7 - $id = $request->getURIData('id'); 8 - 9 - $post = id(new PhamePostQuery()) 10 - ->setViewer($viewer) 11 - ->withIDs(array($id)) 12 - ->requireCapabilities( 13 - array( 14 - PhabricatorPolicyCapability::CAN_EDIT, 15 - )) 16 - ->executeOne(); 17 - if (!$post) { 18 - return new Aphront404Response(); 19 - } 20 - 21 - $view_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/'); 22 - 23 - if ($request->isFormPost()) { 24 - $xactions = array(); 25 - $xactions[] = id(new PhamePostTransaction()) 26 - ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) 27 - ->setNewValue(PhameConstants::VISIBILITY_PUBLISHED); 28 - 29 - id(new PhamePostEditor()) 30 - ->setActor($viewer) 31 - ->setContentSourceFromRequest($request) 32 - ->setContinueOnNoEffect(true) 33 - ->setContinueOnMissingFields(true) 34 - ->applyTransactions($post, $xactions); 35 - 36 - return id(new AphrontRedirectResponse())->setURI($view_uri); 37 - } 38 - 39 - $form = id(new AphrontFormView()) 40 - ->setUser($viewer) 41 - ->appendChild( 42 - id(new AphrontFormSubmitControl()) 43 - ->setValue(pht('Publish Post')) 44 - ->addCancelButton($view_uri)); 45 - 46 - $frame = $this->renderPreviewFrame($post); 47 - 48 - $form_box = id(new PHUIObjectBoxView()) 49 - ->setHeaderText(pht('Preview Post')) 50 - ->setForm($form); 51 - 52 - $blog = $post->getBlog(); 53 - 54 - $crumbs = $this->buildApplicationCrumbs(); 55 - $crumbs->addTextCrumb( 56 - $blog->getName(), 57 - $this->getApplicationURI('blog/view/'.$blog->getID().'/')); 58 - $crumbs->addTextCrumb(pht('Preview Post'), $view_uri); 59 - 60 - return $this->newPage() 61 - ->setTitle(pht('Preview Post')) 62 - ->setCrumbs($crumbs) 63 - ->appendChild( 64 - array( 65 - $form_box, 66 - $frame, 67 - )); 68 - } 69 - 70 - private function renderPreviewFrame(PhamePost $post) { 71 - 72 - return phutil_tag( 73 - 'div', 74 - array( 75 - 'style' => 'text-align: center; padding: 16px;', 76 - ), 77 - phutil_tag( 78 - 'iframe', 79 - array( 80 - 'style' => 'width: 100%; height: 800px; '. 81 - 'border: 1px solid #BFCFDA; '. 82 - 'background-color: #fff; '. 83 - 'border-radius: 3px; ', 84 - 'src' => $this->getApplicationURI('/post/framed/'.$post->getID().'/'), 85 - ), 86 - '')); 87 - } 88 - 89 - }
+31 -13
src/applications/phame/controller/post/PhamePostPublishController.php
··· 4 4 5 5 public function handleRequest(AphrontRequest $request) { 6 6 $viewer = $request->getViewer(); 7 - $id = $request->getURIData('id'); 8 7 8 + $id = $request->getURIData('id'); 9 9 $post = id(new PhamePostQuery()) 10 10 ->setViewer($viewer) 11 11 ->withIDs(array($id)) 12 12 ->requireCapabilities( 13 13 array( 14 + PhabricatorPolicyCapability::CAN_VIEW, 14 15 PhabricatorPolicyCapability::CAN_EDIT, 15 16 )) 16 17 ->executeOne(); ··· 18 19 return new Aphront404Response(); 19 20 } 20 21 22 + $cancel_uri = $post->getViewURI(); 23 + 24 + $action = $request->getURIData('action'); 25 + $is_publish = ($action == 'publish'); 26 + 21 27 if ($request->isFormPost()) { 22 28 $xactions = array(); 29 + 30 + if ($is_publish) { 31 + $new_value = PhameConstants::VISIBILITY_PUBLISHED; 32 + } else { 33 + $new_value = PhameConstants::VISIBILITY_DRAFT; 34 + } 35 + 23 36 $xactions[] = id(new PhamePostTransaction()) 24 37 ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) 25 - ->setNewValue(PhameConstants::VISIBILITY_PUBLISHED); 38 + ->setNewValue($new_value); 26 39 27 40 id(new PhamePostEditor()) 28 41 ->setActor($viewer) ··· 32 45 ->applyTransactions($post, $xactions); 33 46 34 47 return id(new AphrontRedirectResponse()) 35 - ->setURI($this->getApplicationURI('/post/view/'.$post->getID().'/')); 48 + ->setURI($cancel_uri); 36 49 } 37 50 38 - $cancel_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/'); 51 + if ($is_publish) { 52 + $title = pht('Publish Post'); 53 + $body = pht('This post will go live once you publish it.'); 54 + $button = pht('Publish'); 55 + } else { 56 + $title = pht('Unpublish Post'); 57 + $body = pht( 58 + 'This post will revert to draft status and no longer be visible '. 59 + 'to other users.'); 60 + $button = pht('Unpublish'); 61 + } 39 62 40 - $dialog = $this->newDialog() 41 - ->setTitle(pht('Publish Post?')) 42 - ->appendChild( 43 - pht( 44 - 'The post "%s" will go live once you publish it.', 45 - $post->getTitle())) 46 - ->addSubmitButton(pht('Publish')) 63 + return $this->newDialog() 64 + ->setTitle($title) 65 + ->appendParagraph($body) 66 + ->addSubmitButton($button) 47 67 ->addCancelButton($cancel_uri); 48 - 49 - return id(new AphrontDialogResponse())->setDialog($dialog); 50 68 } 51 69 52 70 }
-53
src/applications/phame/controller/post/PhamePostUnpublishController.php
··· 1 - <?php 2 - 3 - final class PhamePostUnpublishController extends PhamePostController { 4 - 5 - public function handleRequest(AphrontRequest $request) { 6 - $viewer = $request->getViewer(); 7 - $id = $request->getURIData('id'); 8 - 9 - $post = id(new PhamePostQuery()) 10 - ->setViewer($viewer) 11 - ->withIDs(array($id)) 12 - ->requireCapabilities( 13 - array( 14 - PhabricatorPolicyCapability::CAN_EDIT, 15 - )) 16 - ->executeOne(); 17 - if (!$post) { 18 - return new Aphront404Response(); 19 - } 20 - 21 - if ($request->isFormPost()) { 22 - $xactions = array(); 23 - $xactions[] = id(new PhamePostTransaction()) 24 - ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) 25 - ->setNewValue(PhameConstants::VISIBILITY_DRAFT); 26 - 27 - id(new PhamePostEditor()) 28 - ->setActor($viewer) 29 - ->setContentSourceFromRequest($request) 30 - ->setContinueOnNoEffect(true) 31 - ->setContinueOnMissingFields(true) 32 - ->applyTransactions($post, $xactions); 33 - 34 - return id(new AphrontRedirectResponse()) 35 - ->setURI($this->getApplicationURI('/post/view/'.$post->getID().'/')); 36 - } 37 - 38 - $cancel_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/'); 39 - 40 - $dialog = $this->newDialog() 41 - ->setTitle(pht('Unpublish Post?')) 42 - ->appendChild( 43 - pht( 44 - 'The post "%s" will no longer be visible to other users until you '. 45 - 'republish it.', 46 - $post->getTitle())) 47 - ->addSubmitButton(pht('Unpublish')) 48 - ->addCancelButton($cancel_uri); 49 - 50 - return id(new AphrontDialogResponse())->setDialog($dialog); 51 - } 52 - 53 - }
+56 -67
src/applications/phame/controller/post/PhamePostViewController.php
··· 1 1 <?php 2 2 3 - final class PhamePostViewController extends PhamePostController { 4 - 5 - public function shouldAllowPublic() { 6 - return true; 7 - } 3 + final class PhamePostViewController 4 + extends PhameLiveController { 8 5 9 6 public function handleRequest(AphrontRequest $request) { 7 + $response = $this->setupLiveEnvironment(); 8 + if ($response) { 9 + return $response; 10 + } 11 + 10 12 $viewer = $request->getViewer(); 11 13 $moved = $request->getStr('moved'); 12 14 13 - $post = id(new PhamePostQuery()) 14 - ->setViewer($viewer) 15 - ->withIDs(array($request->getURIData('id'))) 16 - ->executeOne(); 15 + $post = $this->getPost(); 16 + $blog = $this->getBlog(); 17 17 18 - if (!$post) { 19 - return new Aphront404Response(); 20 - } 18 + $is_live = $this->getIsLive(); 19 + $is_external = $this->getIsExternal(); 21 20 22 - $blog = $post->getBlog(); 21 + $header = id(new PHUIHeaderView()) 22 + ->setHeader($post->getTitle()) 23 + ->setUser($viewer); 23 24 24 - $crumbs = $this->buildApplicationCrumbs(); 25 - $crumbs->addTextCrumb( 26 - pht('Blogs'), 27 - $this->getApplicationURI('blog/')); 28 - $crumbs->addTextCrumb( 29 - $blog->getName(), 30 - $this->getApplicationURI('blog/view/'.$blog->getID().'/')); 31 - $crumbs->addTextCrumb( 32 - $post->getTitle(), 33 - $this->getApplicationURI('post/view/'.$post->getID().'/')); 34 - $crumbs->setBorder(true); 25 + if (!$is_external) { 26 + $actions = $this->renderActions($post); 35 27 36 - $actions = $this->renderActions($post, $viewer); 28 + $action_button = id(new PHUIButtonView()) 29 + ->setTag('a') 30 + ->setText(pht('Actions')) 31 + ->setHref('#') 32 + ->setIconFont('fa-bars') 33 + ->addClass('phui-mobile-menu') 34 + ->setDropdownMenu($actions); 37 35 38 - $action_button = id(new PHUIButtonView()) 39 - ->setTag('a') 40 - ->setText(pht('Actions')) 41 - ->setHref('#') 42 - ->setIconFont('fa-bars') 43 - ->addClass('phui-mobile-menu') 44 - ->setDropdownMenu($actions); 45 - 46 - $header = id(new PHUIHeaderView()) 47 - ->setHeader($post->getTitle()) 48 - ->setUser($viewer) 49 - ->setPolicyObject($post) 50 - ->addActionLink($action_button); 36 + $header->setPolicyObject($post); 37 + $header->addActionLink($action_button); 38 + } 51 39 52 40 $document = id(new PHUIDocumentViewPro()) 53 41 ->setHeader($header); ··· 66 54 ->setTitle(pht('Draft Post')) 67 55 ->appendChild( 68 56 pht('Only you can see this draft until you publish it. '. 69 - 'Use "Preview" or "Publish" to publish this post.'))); 57 + 'Use "Publish" to publish this post.'))); 70 58 } 71 59 72 60 if (!$post->getBlog()) { ··· 125 113 ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); 126 114 $timeline = phutil_tag_div('phui-document-view-pro-box', $timeline); 127 115 128 - $add_comment = $this->buildCommentForm($post); 129 - $add_comment = phutil_tag_div('mlb mlt', $add_comment); 116 + if ($is_external) { 117 + $add_comment = null; 118 + } else { 119 + $add_comment = $this->buildCommentForm($post); 120 + $add_comment = phutil_tag_div('mlb mlt', $add_comment); 121 + } 130 122 131 123 $properties = id(new PHUIPropertyListView()) 132 124 ->setUser($viewer) ··· 134 126 135 127 $properties->invokeWillRenderEvent(); 136 128 137 - return $this->newPage() 129 + $crumbs = $this->buildApplicationCrumbs(); 130 + 131 + $page = $this->newPage() 138 132 ->setTitle($post->getTitle()) 139 133 ->setPageObjectPHIDs(array($post->getPHID())) 140 134 ->setCrumbs($crumbs) ··· 146 140 $timeline, 147 141 $add_comment, 148 142 )); 143 + 144 + if ($is_live) { 145 + $page 146 + ->setShowChrome(false) 147 + ->setShowFooter(false); 148 + } 149 + 150 + return $page; 149 151 } 150 152 151 - private function renderActions( 152 - PhamePost $post, 153 - PhabricatorUser $viewer) { 153 + private function renderActions(PhamePost $post) { 154 + $viewer = $this->getViewer(); 154 155 155 - $actions = id(new PhabricatorActionListView()) 156 - ->setObject($post) 157 - ->setObjectURI($this->getRequest()->getRequestURI()) 158 - ->setUser($viewer); 156 + $actions = id(new PhabricatorActionListView()) 157 + ->setObject($post) 158 + ->setObjectURI($this->getRequest()->getRequestURI()) 159 + ->setUser($viewer); 159 160 160 161 $can_edit = PhabricatorPolicyFilter::hasCapability( 161 162 $viewer, ··· 190 191 id(new PhabricatorActionView()) 191 192 ->setIcon('fa-eye') 192 193 ->setHref($this->getApplicationURI('post/publish/'.$id.'/')) 193 - ->setDisabled(!$can_edit) 194 194 ->setName(pht('Publish')) 195 - ->setWorkflow(true)); 196 - $actions->addAction( 197 - id(new PhabricatorActionView()) 198 - ->setIcon('fa-desktop') 199 - ->setHref($this->getApplicationURI('post/preview/'.$id.'/')) 200 195 ->setDisabled(!$can_edit) 201 - ->setName(pht('Preview in Skin'))); 196 + ->setWorkflow(true)); 202 197 } else { 203 198 $actions->addAction( 204 199 id(new PhabricatorActionView()) ··· 209 204 ->setWorkflow(true)); 210 205 } 211 206 212 - $blog = $post->getBlog(); 213 - $can_view_live = $blog && !$post->isDraft(); 214 - 215 - if ($can_view_live) { 216 - $live_uri = $blog->getLiveURI($post); 207 + if ($post->isDraft()) { 208 + $live_name = pht('Preview'); 217 209 } else { 218 - $live_uri = 'post/notlive/'.$post->getID().'/'; 219 - $live_uri = $this->getApplicationURI($live_uri); 210 + $live_name = pht('View Live'); 220 211 } 221 212 222 213 $actions->addAction( 223 214 id(new PhabricatorActionView()) 224 215 ->setUser($viewer) 225 216 ->setIcon('fa-globe') 226 - ->setHref($live_uri) 227 - ->setName(pht('View Live')) 228 - ->setDisabled(!$can_view_live) 229 - ->setWorkflow(!$can_view_live)); 217 + ->setHref($post->getLiveURI()) 218 + ->setName($live_name)); 230 219 231 220 return $actions; 232 221 }
-7
src/applications/phame/editor/PhameBlogEditor.php
··· 17 17 $types[] = PhameBlogTransaction::TYPE_NAME; 18 18 $types[] = PhameBlogTransaction::TYPE_DESCRIPTION; 19 19 $types[] = PhameBlogTransaction::TYPE_DOMAIN; 20 - $types[] = PhameBlogTransaction::TYPE_SKIN; 21 20 $types[] = PhameBlogTransaction::TYPE_STATUS; 22 21 $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; 23 22 $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; ··· 36 35 return $object->getDescription(); 37 36 case PhameBlogTransaction::TYPE_DOMAIN: 38 37 return $object->getDomain(); 39 - case PhameBlogTransaction::TYPE_SKIN: 40 - return $object->getSkin(); 41 38 case PhameBlogTransaction::TYPE_STATUS: 42 39 return $object->getStatus(); 43 40 } ··· 51 48 case PhameBlogTransaction::TYPE_NAME: 52 49 case PhameBlogTransaction::TYPE_DESCRIPTION: 53 50 case PhameBlogTransaction::TYPE_DOMAIN: 54 - case PhameBlogTransaction::TYPE_SKIN: 55 51 case PhameBlogTransaction::TYPE_STATUS: 56 52 return $xaction->getNewValue(); 57 53 } ··· 68 64 return $object->setDescription($xaction->getNewValue()); 69 65 case PhameBlogTransaction::TYPE_DOMAIN: 70 66 return $object->setDomain($xaction->getNewValue()); 71 - case PhameBlogTransaction::TYPE_SKIN: 72 - return $object->setSkin($xaction->getNewValue()); 73 67 case PhameBlogTransaction::TYPE_STATUS: 74 68 return $object->setStatus($xaction->getNewValue()); 75 69 } ··· 85 79 case PhameBlogTransaction::TYPE_NAME: 86 80 case PhameBlogTransaction::TYPE_DESCRIPTION: 87 81 case PhameBlogTransaction::TYPE_DOMAIN: 88 - case PhameBlogTransaction::TYPE_SKIN: 89 82 case PhameBlogTransaction::TYPE_STATUS: 90 83 return; 91 84 }
+5
src/applications/phame/site/PhameBlogSite.php
··· 38 38 $blog = id(new PhameBlogQuery()) 39 39 ->setViewer(new PhabricatorUser()) 40 40 ->withDomain($host) 41 + ->needProfileImage(true) 42 + ->withStatuses( 43 + array( 44 + PhameBlog::STATUS_ACTIVE, 45 + )) 41 46 ->executeOne(); 42 47 } catch (PhabricatorPolicyException $ex) { 43 48 throw new Exception(
-325
src/applications/phame/skins/PhameBasicBlogSkin.php
··· 1 - <?php 2 - 3 - /** 4 - * @task paging Paging 5 - * @task internal Internals 6 - */ 7 - abstract class PhameBasicBlogSkin extends PhameBlogSkin { 8 - 9 - private $pager; 10 - private $title; 11 - private $description; 12 - private $oGType; 13 - private $uriPath; 14 - 15 - public function setURIPath($uri_path) { 16 - $this->uriPath = $uri_path; 17 - return $this; 18 - } 19 - 20 - public function getURIPath() { 21 - return $this->uriPath; 22 - } 23 - 24 - protected function setOGType($og_type) { 25 - $this->oGType = $og_type; 26 - return $this; 27 - } 28 - 29 - protected function getOGType() { 30 - return $this->oGType; 31 - } 32 - 33 - protected function setDescription($description) { 34 - $this->description = $description; 35 - return $this; 36 - } 37 - 38 - protected function getDescription() { 39 - return $this->description; 40 - } 41 - 42 - protected function setTitle($title) { 43 - $this->title = $title; 44 - return $this; 45 - } 46 - 47 - protected function getTitle() { 48 - return $this->title; 49 - } 50 - 51 - public function handleRequest(AphrontRequest $request) { 52 - $content = $this->renderContent($request); 53 - 54 - if (!$content) { 55 - $content = $this->render404Page(); 56 - } 57 - 58 - $content = array( 59 - $this->renderHeader(), 60 - $content, 61 - $this->renderFooter(), 62 - ); 63 - 64 - $view = id(new PhabricatorBarePageView()) 65 - ->setRequest($request) 66 - ->setController($this) 67 - ->setDeviceReady(true) 68 - ->setTitle($this->getBlog()->getName()); 69 - 70 - if ($this->getPreview()) { 71 - $view->setFrameable(true); 72 - } 73 - 74 - $view->appendChild($content); 75 - 76 - $response = new AphrontWebpageResponse(); 77 - $response->setContent($view->render()); 78 - 79 - return $response; 80 - } 81 - 82 - public function getSkinName() { 83 - return get_class($this); 84 - } 85 - 86 - abstract protected function renderHeader(); 87 - abstract protected function renderFooter(); 88 - 89 - protected function renderPostDetail(PhamePostView $post) { 90 - return $post; 91 - } 92 - 93 - protected function renderPostList(array $posts) { 94 - $summaries = array(); 95 - foreach ($posts as $post) { 96 - $summaries[] = $post->renderWithSummary(); 97 - } 98 - 99 - $list = phutil_tag( 100 - 'div', 101 - array( 102 - 'class' => 'phame-post-list', 103 - ), 104 - id(new AphrontNullView())->appendChild($summaries)->render()); 105 - 106 - $pager = null; 107 - if ($this->renderOlderPageLink() || $this->renderNewerPageLink()) { 108 - $pager = phutil_tag( 109 - 'div', 110 - array( 111 - 'class' => 'phame-pager', 112 - ), 113 - array( 114 - $this->renderOlderPageLink(), 115 - $this->renderNewerPageLink(), 116 - )); 117 - } 118 - 119 - return array( 120 - $list, 121 - $pager, 122 - ); 123 - } 124 - 125 - protected function render404Page() { 126 - return phutil_tag('h2', array(), pht('404 Not Found')); 127 - } 128 - 129 - final public function getResourceURI($resource) { 130 - $root = $this->getSpecification()->getRootDirectory(); 131 - $path = $root.DIRECTORY_SEPARATOR.$resource; 132 - 133 - $data = Filesystem::readFile($path); 134 - $hash = PhabricatorHash::digest($data); 135 - $hash = substr($hash, 0, 6); 136 - $id = $this->getBlog()->getID(); 137 - 138 - $uri = '/phame/r/'.$id.'/'.$hash.'/'.$resource; 139 - $uri = PhabricatorEnv::getCDNURI($uri); 140 - 141 - return $uri; 142 - } 143 - 144 - /* -( Paging )------------------------------------------------------------- */ 145 - 146 - 147 - /** 148 - * @task paging 149 - */ 150 - public function getPageSize() { 151 - return 100; 152 - } 153 - 154 - 155 - /** 156 - * @task paging 157 - */ 158 - protected function getOlderPageURI() { 159 - if ($this->pager) { 160 - $next = $this->pager->getNextPageID(); 161 - if ($next) { 162 - return $this->getURI('older/'.$next.'/'); 163 - } 164 - } 165 - return null; 166 - } 167 - 168 - 169 - /** 170 - * @task paging 171 - */ 172 - protected function renderOlderPageLink() { 173 - $uri = $this->getOlderPageURI(); 174 - if (!$uri) { 175 - return null; 176 - } 177 - return phutil_tag( 178 - 'a', 179 - array( 180 - 'class' => 'phame-page-link phame-page-older', 181 - 'href' => $uri, 182 - ), 183 - pht("\xE2\x80\xB9 Older")); 184 - } 185 - 186 - 187 - /** 188 - * @task paging 189 - */ 190 - protected function getNewerPageURI() { 191 - if ($this->pager) { 192 - $next = $this->pager->getPrevPageID(); 193 - if ($next) { 194 - return $this->getURI('newer/'.$next.'/'); 195 - } 196 - } 197 - return null; 198 - } 199 - 200 - 201 - /** 202 - * @task paging 203 - */ 204 - protected function renderNewerPageLink() { 205 - $uri = $this->getNewerPageURI(); 206 - if (!$uri) { 207 - return null; 208 - } 209 - return phutil_tag( 210 - 'a', 211 - array( 212 - 'class' => 'phame-page-link phame-page-newer', 213 - 'href' => $uri, 214 - ), 215 - pht("Newer \xE2\x80\xBA")); 216 - } 217 - 218 - 219 - /* -( Internals )---------------------------------------------------------- */ 220 - 221 - 222 - /** 223 - * @task internal 224 - */ 225 - protected function renderContent(AphrontRequest $request) { 226 - $viewer = $request->getViewer(); 227 - 228 - $matches = null; 229 - $path = $request->getPath(); 230 - // default to the blog-wide values 231 - $this->setTitle($this->getBlog()->getName()); 232 - $this->setDescription($this->getBlog()->getDescription()); 233 - $this->setOGType('website'); 234 - $this->setURIPath(''); 235 - if (preg_match('@^/post/(?P<name>.*)$@', $path, $matches)) { 236 - $post = id(new PhamePostQuery()) 237 - ->setViewer($viewer) 238 - ->withBlogPHIDs(array($this->getBlog()->getPHID())) 239 - ->withPhameTitles(array($matches['name'])) 240 - ->executeOne(); 241 - 242 - if ($post) { 243 - $description = $post->getMarkupText(PhamePost::MARKUP_FIELD_SUMMARY); 244 - $this->setTitle($post->getTitle()); 245 - $this->setDescription($description); 246 - $this->setOGType('article'); 247 - $this->setURIPath('post/'.$post->getPhameTitle()); 248 - $view = head($this->buildPostViews(array($post))); 249 - return $this->renderPostDetail($view); 250 - } 251 - } else { 252 - $pager = new AphrontCursorPagerView(); 253 - 254 - if (preg_match('@^/older/(?P<before>\d+)/$@', $path, $matches)) { 255 - $pager->setAfterID($matches['before']); 256 - } else if (preg_match('@^/newer/(?P<after>\d)/$@', $path, $matches)) { 257 - $pager->setBeforeID($matches['after']); 258 - } else if (preg_match('@^/$@', $path, $matches)) { 259 - // Just show the first page. 260 - } else { 261 - return null; 262 - } 263 - 264 - $pager->setPageSize($this->getPageSize()); 265 - 266 - $posts = id(new PhamePostQuery()) 267 - ->setViewer($viewer) 268 - ->withBlogPHIDs(array($this->getBlog()->getPHID())) 269 - ->executeWithCursorPager($pager); 270 - 271 - $this->pager = $pager; 272 - 273 - if ($posts) { 274 - $views = $this->buildPostViews($posts); 275 - return $this->renderPostList($views); 276 - } 277 - } 278 - 279 - return null; 280 - } 281 - 282 - private function buildPostViews(array $posts) { 283 - assert_instances_of($posts, 'PhamePost'); 284 - $viewer = $this->getViewer(); 285 - 286 - $engine = id(new PhabricatorMarkupEngine()) 287 - ->setViewer($viewer); 288 - 289 - $phids = array(); 290 - foreach ($posts as $post) { 291 - $engine->addObject($post, PhamePost::MARKUP_FIELD_BODY); 292 - $engine->addObject($post, PhamePost::MARKUP_FIELD_SUMMARY); 293 - 294 - $phids[] = $post->getBloggerPHID(); 295 - } 296 - 297 - $handles = id(new PhabricatorHandleQuery()) 298 - ->setViewer($viewer) 299 - ->withPHIDs($phids) 300 - ->execute(); 301 - 302 - $engine->process(); 303 - 304 - $views = array(); 305 - foreach ($posts as $post) { 306 - $view = id(new PhamePostView()) 307 - ->setUser($viewer) 308 - ->setSkin($this) 309 - ->setPost($post) 310 - ->setBody($engine->getOutput($post, PhamePost::MARKUP_FIELD_BODY)) 311 - ->setSummary($engine->getOutput($post, PhamePost::MARKUP_FIELD_SUMMARY)) 312 - ->setAuthor($handles[$post->getBloggerPHID()]); 313 - 314 - $post->makeEphemeral(); 315 - if (!$post->getDatePublished()) { 316 - $post->setDatePublished(time()); 317 - } 318 - 319 - $views[] = $view; 320 - } 321 - 322 - return $views; 323 - } 324 - 325 - }
-161
src/applications/phame/skins/PhameBasicTemplateBlogSkin.php
··· 1 - <?php 2 - 3 - final class PhameBasicTemplateBlogSkin extends PhameBasicBlogSkin { 4 - 5 - private $cssResources; 6 - 7 - public function processRequest() { 8 - $root = dirname(phutil_get_library_root('phabricator')); 9 - require_once $root.'/support/phame/libskin.php'; 10 - 11 - $this->cssResources = array(); 12 - $css = $this->getPath('css/'); 13 - 14 - if (Filesystem::pathExists($css)) { 15 - foreach (Filesystem::listDirectory($css) as $path) { 16 - if (!preg_match('/.css$/', $path)) { 17 - continue; 18 - } 19 - $this->cssResources[] = phutil_tag( 20 - 'link', 21 - array( 22 - 'rel' => 'stylesheet', 23 - 'type' => 'text/css', 24 - 'href' => $this->getResourceURI('css/'.$path), 25 - )); 26 - } 27 - } 28 - 29 - $map = CelerityResourceMap::getNamedInstance('phabricator'); 30 - $highlight_symbol = 'syntax-highlighting-css'; 31 - $highlight_uri = $map->getURIForSymbol($highlight_symbol); 32 - 33 - $this->cssResources[] = phutil_tag( 34 - 'link', 35 - array( 36 - 'rel' => 'stylesheet', 37 - 'type' => 'text/css', 38 - 'href' => PhabricatorEnv::getCDNURI($highlight_uri), 39 - )); 40 - 41 - $remarkup_symbol = 'phabricator-remarkup-css'; 42 - $remarkup_uri = $map->getURIForSymbol($remarkup_symbol); 43 - 44 - $this->cssResources[] = phutil_tag( 45 - 'link', 46 - array( 47 - 'rel' => 'stylesheet', 48 - 'type' => 'text/css', 49 - 'href' => PhabricatorEnv::getCDNURI($remarkup_uri), 50 - )); 51 - 52 - $this->cssResources = phutil_implode_html("\n", $this->cssResources); 53 - 54 - $request = $this->getRequest(); 55 - 56 - // Render page parts in order so the templates execute in order, if we're 57 - // using templates. 58 - $header = $this->renderHeader(); 59 - $content = $this->renderContent($request); 60 - $footer = $this->renderFooter(); 61 - 62 - if (!$content) { 63 - $content = $this->render404Page(); 64 - } 65 - 66 - $content = array( 67 - $header, 68 - $content, 69 - $footer, 70 - ); 71 - 72 - $response = new AphrontWebpageResponse(); 73 - $response->setContent(phutil_implode_html("\n", $content)); 74 - 75 - return $response; 76 - } 77 - 78 - public function getCSSResources() { 79 - return $this->cssResources; 80 - } 81 - 82 - public function remarkup($corpus) { 83 - $view = id(new PHUIRemarkupView($this->getViewer(), $corpus)); 84 - 85 - return hsprintf('%s', $view); 86 - } 87 - 88 - public function getName() { 89 - return $this->getSpecification()->getName(); 90 - } 91 - 92 - public function getPath($to_file = null) { 93 - $path = $this->getSpecification()->getRootDirectory(); 94 - if ($to_file) { 95 - $path = $path.DIRECTORY_SEPARATOR.$to_file; 96 - } 97 - return $path; 98 - } 99 - 100 - private function renderTemplate($__template__, array $__scope__) { 101 - chdir($this->getPath()); 102 - ob_start(); 103 - 104 - if (Filesystem::pathExists($this->getPath($__template__))) { 105 - // Fool lint. 106 - $__evil__ = 'extract'; 107 - $__evil__($__scope__ + $this->getDefaultScope()); 108 - require $this->getPath($__template__); 109 - } 110 - 111 - return phutil_safe_html(ob_get_clean()); 112 - } 113 - 114 - private function getDefaultScope() { 115 - return array( 116 - 'skin' => $this, 117 - 'blog' => $this->getBlog(), 118 - 'uri' => $this->getURI($this->getURIPath()), 119 - 'home_uri' => $this->getURI(''), 120 - 121 - // TODO: This is wrong for detail pages, which should show the post 122 - // title, but getting it right is a pain and this is better than nothing. 123 - 'title' => $this->getBlog()->getName(), 124 - 'description' => $this->getDescription(), 125 - 'og_type' => $this->getOGType(), 126 - ); 127 - } 128 - 129 - protected function renderHeader() { 130 - return $this->renderTemplate( 131 - 'header.php', 132 - array()); 133 - } 134 - 135 - protected function renderFooter() { 136 - return $this->renderTemplate('footer.php', array()); 137 - } 138 - 139 - protected function render404Page() { 140 - return $this->renderTemplate('404.php', array()); 141 - } 142 - 143 - protected function renderPostDetail(PhamePostView $post) { 144 - return $this->renderTemplate( 145 - 'post-detail.php', 146 - array( 147 - 'post' => $post, 148 - )); 149 - } 150 - 151 - protected function renderPostList(array $posts) { 152 - return $this->renderTemplate( 153 - 'post-list.php', 154 - array( 155 - 'posts' => $posts, 156 - 'older' => $this->renderOlderPageLink(), 157 - 'newer' => $this->renderNewerPageLink(), 158 - )); 159 - } 160 - 161 - }
-46
src/applications/phame/skins/PhameBlogSkin.php
··· 1 - <?php 2 - 3 - abstract class PhameBlogSkin extends PhabricatorController { 4 - 5 - private $blog; 6 - private $baseURI; 7 - private $preview; 8 - private $specification; 9 - 10 - public function setSpecification(PhameSkinSpecification $specification) { 11 - $this->specification = $specification; 12 - return $this; 13 - } 14 - 15 - public function getSpecification() { 16 - return $this->specification; 17 - } 18 - 19 - public function setPreview($preview) { 20 - $this->preview = $preview; 21 - return $this; 22 - } 23 - 24 - public function getPreview() { 25 - return $this->preview; 26 - } 27 - 28 - final public function setBaseURI($base_uri) { 29 - $this->baseURI = $base_uri; 30 - return $this; 31 - } 32 - 33 - final public function getURI($path) { 34 - return $this->baseURI.$path; 35 - } 36 - 37 - final public function setBlog(PhameBlog $blog) { 38 - $this->blog = $blog; 39 - return $this; 40 - } 41 - 42 - final public function getBlog() { 43 - return $this->blog; 44 - } 45 - 46 - }
-198
src/applications/phame/skins/PhameSkinSpecification.php
··· 1 - <?php 2 - 3 - final class PhameSkinSpecification extends Phobject { 4 - 5 - const TYPE_ADVANCED = 'advanced'; 6 - const TYPE_BASIC = 'basic'; 7 - const SKIN_PATH = 'externals/skins/'; 8 - 9 - private $type; 10 - private $rootDirectory; 11 - private $skinClass; 12 - private $phutilLibraries = array(); 13 - private $name; 14 - private $config; 15 - 16 - public static function loadAllSkinSpecifications() { 17 - static $specs; 18 - 19 - if ($specs === null) { 20 - $paths = array(self::SKIN_PATH); 21 - $base = dirname(phutil_get_library_root('phabricator')); 22 - 23 - $specs = array(); 24 - 25 - foreach ($paths as $path) { 26 - $path = Filesystem::resolvePath($path, $base); 27 - foreach (Filesystem::listDirectory($path) as $skin_directory) { 28 - $skin_path = $path.DIRECTORY_SEPARATOR.$skin_directory; 29 - 30 - if (!is_dir($skin_path)) { 31 - continue; 32 - } 33 - $spec = self::loadSkinSpecification($skin_path); 34 - if (!$spec) { 35 - continue; 36 - } 37 - 38 - $name = trim($skin_directory, DIRECTORY_SEPARATOR); 39 - 40 - $spec->setName($name); 41 - 42 - if (isset($specs[$name])) { 43 - $that_dir = $specs[$name]->getRootDirectory(); 44 - $this_dir = $spec->getRootDirectory(); 45 - throw new Exception( 46 - pht( 47 - "Two skins have the same name ('%s'), in '%s' and '%s'. ". 48 - "Rename one or adjust your '%s' configuration.", 49 - $name, 50 - $this_dir, 51 - $that_dir, 52 - self::SKIN_PATH)); 53 - } 54 - 55 - $specs[$name] = $spec; 56 - } 57 - } 58 - } 59 - 60 - return $specs; 61 - } 62 - 63 - public static function loadOneSkinSpecification($name) { 64 - // Only allow skins which we know to exist to load. This prevents loading 65 - // skins like "../../secrets/evil/". 66 - $all = self::loadAllSkinSpecifications(); 67 - if (empty($all[$name])) { 68 - throw new Exception( 69 - pht( 70 - 'Blog skin "%s" is not a valid skin!', 71 - $name)); 72 - } 73 - 74 - $paths = array(self::SKIN_PATH); 75 - $base = dirname(phutil_get_library_root('phabricator')); 76 - foreach ($paths as $path) { 77 - $path = Filesystem::resolvePath($path, $base); 78 - $skin_path = $path.DIRECTORY_SEPARATOR.$name; 79 - if (is_dir($skin_path)) { 80 - 81 - // Double check that the skin really lives in the skin directory. 82 - if (!Filesystem::isDescendant($skin_path, $path)) { 83 - throw new Exception( 84 - pht( 85 - 'Blog skin "%s" is not located in path "%s"!', 86 - $name, 87 - $path)); 88 - } 89 - 90 - $spec = self::loadSkinSpecification($skin_path); 91 - if ($spec) { 92 - $spec->setName($name); 93 - return $spec; 94 - } 95 - } 96 - } 97 - return null; 98 - } 99 - 100 - private static function loadSkinSpecification($path) { 101 - $config_path = $path.DIRECTORY_SEPARATOR.'skin.json'; 102 - $config = array(); 103 - if (Filesystem::pathExists($config_path)) { 104 - $config = Filesystem::readFile($config_path); 105 - try { 106 - $config = phutil_json_decode($config); 107 - } catch (PhutilJSONParserException $ex) { 108 - throw new PhutilProxyException( 109 - pht( 110 - "Skin configuration file '%s' is not a valid JSON file.", 111 - $config_path), 112 - $ex); 113 - } 114 - $type = idx($config, 'type', self::TYPE_BASIC); 115 - } else { 116 - $type = self::TYPE_BASIC; 117 - } 118 - 119 - $spec = new PhameSkinSpecification(); 120 - $spec->setRootDirectory($path); 121 - $spec->setConfig($config); 122 - 123 - switch ($type) { 124 - case self::TYPE_BASIC: 125 - $spec->setSkinClass('PhameBasicTemplateBlogSkin'); 126 - break; 127 - case self::TYPE_ADVANCED: 128 - $spec->setSkinClass($config['class']); 129 - $spec->addPhutilLibrary($path.DIRECTORY_SEPARATOR.'src'); 130 - break; 131 - default: 132 - throw new Exception(pht('Unknown skin type!')); 133 - } 134 - 135 - $spec->setType($type); 136 - 137 - return $spec; 138 - } 139 - 140 - public function setConfig(array $config) { 141 - $this->config = $config; 142 - return $this; 143 - } 144 - 145 - public function getConfig($key, $default = null) { 146 - return idx($this->config, $key, $default); 147 - } 148 - 149 - public function setName($name) { 150 - $this->name = $name; 151 - return $this; 152 - } 153 - 154 - public function getName() { 155 - return $this->getConfig('name', $this->name); 156 - } 157 - 158 - public function setRootDirectory($root_directory) { 159 - $this->rootDirectory = $root_directory; 160 - return $this; 161 - } 162 - 163 - public function getRootDirectory() { 164 - return $this->rootDirectory; 165 - } 166 - 167 - public function setType($type) { 168 - $this->type = $type; 169 - return $this; 170 - } 171 - 172 - public function getType() { 173 - return $this->type; 174 - } 175 - 176 - public function setSkinClass($skin_class) { 177 - $this->skinClass = $skin_class; 178 - return $this; 179 - } 180 - 181 - public function getSkinClass() { 182 - return $this->skinClass; 183 - } 184 - 185 - public function addPhutilLibrary($library) { 186 - $this->phutilLibraries[] = $library; 187 - return $this; 188 - } 189 - 190 - public function buildSkin(AphrontRequest $request) { 191 - foreach ($this->phutilLibraries as $library) { 192 - phutil_load_library($library); 193 - } 194 - 195 - return newv($this->getSkinClass(), array($request, $this)); 196 - } 197 - 198 - }
+13 -65
src/applications/phame/storage/PhameBlog.php
··· 11 11 PhabricatorApplicationTransactionInterface { 12 12 13 13 const MARKUP_FIELD_DESCRIPTION = 'markup:description'; 14 - const SKIN_DEFAULT = 'oblivious'; 15 14 16 15 protected $name; 17 16 protected $description; ··· 25 24 protected $profileImagePHID; 26 25 27 26 private $profileImageFile = self::ATTACHABLE; 28 - private static $requestBlog; 29 27 30 28 const STATUS_ACTIVE = 'active'; 31 29 const STATUS_ARCHIVED = 'archived'; ··· 84 82 return $blog; 85 83 } 86 84 87 - public function getSkinRenderer(AphrontRequest $request) { 88 - $spec = PhameSkinSpecification::loadOneSkinSpecification( 89 - $this->getSkin()); 90 - 91 - if (!$spec) { 92 - $spec = PhameSkinSpecification::loadOneSkinSpecification( 93 - self::SKIN_DEFAULT); 94 - } 95 - 96 - if (!$spec) { 97 - throw new Exception( 98 - pht( 99 - 'This blog has an invalid skin, and the default skin failed to '. 100 - 'load.')); 101 - } 102 - 103 - $skin = newv($spec->getSkinClass(), array()); 104 - $skin->setRequest($request); 105 - $skin->setSpecification($spec); 106 - 107 - return $skin; 108 - } 109 - 110 85 public function isArchived() { 111 86 return ($this->getStatus() == self::STATUS_ARCHIVED); 112 87 } ··· 196 171 return null; 197 172 } 198 173 199 - public function getSkin() { 200 - $config = coalesce($this->getConfigData(), array()); 201 - return idx($config, 'skin', self::SKIN_DEFAULT); 202 - } 203 - 204 - public function setSkin($skin) { 205 - $config = coalesce($this->getConfigData(), array()); 206 - $config['skin'] = $skin; 207 - return $this->setConfigData($config); 208 - } 209 - 210 - public static function getSkinOptionsForSelect() { 211 - $classes = id(new PhutilSymbolLoader()) 212 - ->setAncestorClass('PhameBlogSkin') 213 - ->setType('class') 214 - ->setConcreteOnly(true) 215 - ->selectSymbolsWithoutLoading(); 216 - 217 - return ipull($classes, 'name', 'name'); 174 + public function getLiveURI() { 175 + if (strlen($this->getDomain())) { 176 + return $this->getExternalLiveURI(); 177 + } else { 178 + return $this->getInternalLiveURI(); 179 + } 218 180 } 219 181 220 - public static function setRequestBlog(PhameBlog $blog) { 221 - self::$requestBlog = $blog; 182 + public function getExternalLiveURI() { 183 + $domain = $this->getDomain(); 184 + $uri = new PhutilURI('http://'.$this->getDomain().'/'); 185 + return (string)$uri; 222 186 } 223 187 224 - public static function getRequestBlog() { 225 - return self::$requestBlog; 226 - } 227 - 228 - public function getLiveURI(PhamePost $post = null) { 229 - if ($this->getDomain()) { 230 - $base = new PhutilURI('http://'.$this->getDomain().'/'); 231 - } else { 232 - $base = '/phame/live/'.$this->getID().'/'; 233 - $base = PhabricatorEnv::getURI($base); 234 - } 235 - 236 - if ($post) { 237 - $base .= '/post/'.$post->getPhameTitle(); 238 - } 239 - 240 - return $base; 188 + public function getInternalLiveURI() { 189 + return '/phame/live/'.$this->getID().'/'; 241 190 } 242 191 243 192 public function getViewURI() { 244 - $uri = '/phame/blog/view/'.$this->getID().'/'; 245 - return PhabricatorEnv::getProductionURI($uri); 193 + return '/phame/blog/view/'.$this->getID().'/'; 246 194 } 247 195 248 196 public function getProfileImageURI() {
-15
src/applications/phame/storage/PhameBlogTransaction.php
··· 6 6 const TYPE_NAME = 'phame.blog.name'; 7 7 const TYPE_DESCRIPTION = 'phame.blog.description'; 8 8 const TYPE_DOMAIN = 'phame.blog.domain'; 9 - const TYPE_SKIN = 'phame.blog.skin'; 10 9 const TYPE_STATUS = 'phame.blog.status'; 11 10 12 11 const MAILTAG_DETAILS = 'phame-blog-details'; ··· 43 42 break; 44 43 case self::TYPE_DESCRIPTION: 45 44 case self::TYPE_DOMAIN: 46 - case self::TYPE_SKIN: 47 45 return 'fa-pencil'; 48 46 case self::TYPE_STATUS: 49 47 if ($new == PhameBlog::STATUS_ARCHIVED) { ··· 82 80 case self::TYPE_NAME: 83 81 case self::TYPE_DESCRIPTION: 84 82 case self::TYPE_DOMAIN: 85 - case self::TYPE_SKIN: 86 83 $tags[] = self::MAILTAG_DETAILS; 87 84 break; 88 85 default: ··· 124 121 $this->renderHandleLink($author_phid), 125 122 $new); 126 123 break; 127 - case self::TYPE_SKIN: 128 - return pht( 129 - '%s updated the blog\'s skin to "%s".', 130 - $this->renderHandleLink($author_phid), 131 - $new); 132 - break; 133 124 case self::TYPE_STATUS: 134 125 switch ($new) { 135 126 case PhameBlog::STATUS_ACTIVE: ··· 178 169 case self::TYPE_DOMAIN: 179 170 return pht( 180 171 '%s updated the domain for %s.', 181 - $this->renderHandleLink($author_phid), 182 - $this->renderHandleLink($object_phid)); 183 - break; 184 - case self::TYPE_SKIN: 185 - return pht( 186 - '%s updated the skin for %s.', 187 172 $this->renderHandleLink($author_phid), 188 173 $this->renderHandleLink($object_phid)); 189 174 break;
+32 -20
src/applications/phame/storage/PhamePost.php
··· 49 49 } 50 50 51 51 public function getLiveURI() { 52 - // go for the pretty uri if we can 53 - $domain = ($this->blog ? $this->blog->getDomain() : ''); 54 - if ($domain) { 55 - $phame_title = PhabricatorSlug::normalize($this->getPhameTitle()); 56 - return 'http://'.$domain.'/post/'.$phame_title; 52 + $blog = $this->getBlog(); 53 + $is_draft = $this->isDraft(); 54 + if (strlen($blog->getDomain()) && !$is_draft) { 55 + return $this->getExternalLiveURI(); 56 + } else { 57 + return $this->getInternalLiveURI(); 57 58 } 58 - $uri = '/phame/post/view/'.$this->getID().'/'; 59 - return PhabricatorEnv::getProductionURI($uri); 59 + } 60 + 61 + public function getExternalLiveURI() { 62 + $id = $this->getID(); 63 + $slug = $this->getSlug(); 64 + $path = "/post/{$id}/{$slug}/"; 65 + 66 + $domain = $this->getBlog()->getDomain(); 67 + 68 + return (string)id(new PhutilURI('http://'.$domain)) 69 + ->setPath($path); 70 + } 71 + 72 + public function getInternalLiveURI() { 73 + $id = $this->getID(); 74 + $slug = $this->getSlug(); 75 + $blog_id = $this->getBlog()->getID(); 76 + return "/phame/live/{$blog_id}/post/{$id}/{$slug}/"; 60 77 } 61 78 62 79 public function getViewURI() { 63 - $phame_title = PhabricatorSlug::normalize($this->getPhameTitle()); 64 - return '/phame/post/view/'.$this->getID().'/'.$phame_title; 80 + $id = $this->getID(); 81 + $slug = $this->getSlug(); 82 + return "/phame/post/view/{$id}/{$slug}/"; 65 83 } 66 84 67 85 public function getEditURI() { ··· 69 87 } 70 88 71 89 public function isDraft() { 72 - return $this->getVisibility() == PhameConstants::VISIBILITY_DRAFT; 73 - } 74 - 75 - public function getHumanName() { 76 - if ($this->isDraft()) { 77 - $name = 'draft'; 78 - } else { 79 - $name = 'post'; 80 - } 81 - 82 - return $name; 90 + return ($this->getVisibility() == PhameConstants::VISIBILITY_DRAFT); 83 91 } 84 92 85 93 protected function getConfiguration() { ··· 136 144 public function generatePHID() { 137 145 return PhabricatorPHID::generateNewPHID( 138 146 PhabricatorPhamePostPHIDType::TYPECONST); 147 + } 148 + 149 + public function getSlug() { 150 + return rtrim($this->getPhameTitle(), '/'); 139 151 } 140 152 141 153 public function toDictionary() {
+51 -12
src/applications/phame/view/PhamePostListView.php
··· 6 6 private $nodata; 7 7 private $viewer; 8 8 private $showBlog = false; 9 + private $isExternal; 10 + private $isLive; 9 11 10 12 public function setPosts($posts) { 11 13 assert_instances_of($posts, 'PhamePost'); ··· 28 30 return $this; 29 31 } 30 32 33 + public function setIsExternal($is_external) { 34 + $this->isExternal = $is_external; 35 + return $this; 36 + } 37 + 38 + public function getIsExternal() { 39 + return $this->isExternal; 40 + } 41 + 42 + public function setIsLive($is_live) { 43 + $this->isLive = $is_live; 44 + return $this; 45 + } 46 + 47 + public function getIsLive() { 48 + return $this->isLive; 49 + } 50 + 31 51 protected function getTagAttributes() { 32 52 return array(); 33 53 } ··· 63 83 $blogger = phutil_tag('strong', array(), $blogger); 64 84 $date = phabricator_datetime($post->getDatePublished(), $viewer); 65 85 66 - $blog = null; 67 - if ($post->getBlog()) { 68 - $blog = phutil_tag( 69 - 'a', 70 - array( 71 - 'href' => '/phame/blog/view/'.$post->getBlog()->getID().'/', 72 - ), 73 - $post->getBlog()->getName()); 86 + $blog = $post->getBlog(); 87 + 88 + if ($this->getIsLive()) { 89 + if ($this->getIsExternal()) { 90 + $blog_uri = $blog->getExternalLiveURI(); 91 + $post_uri = $post->getExternalLiveURI(); 92 + } else { 93 + $blog_uri = $blog->getInternalLiveURI(); 94 + $post_uri = $post->getInternalLiveURI(); 95 + } 96 + } else { 97 + $blog_uri = $blog->getViewURI(); 98 + $post_uri = $post->getViewURI(); 74 99 } 75 100 76 - if ($this->showBlog && $blog) { 101 + $blog_link = phutil_tag( 102 + 'a', 103 + array( 104 + 'href' => $blog_uri, 105 + ), 106 + $blog->getName()); 107 + 108 + if ($this->showBlog) { 77 109 if ($post->isDraft()) { 78 - $subtitle = pht('Unpublished draft by %s in %s.', $blogger, $blog); 110 + $subtitle = pht( 111 + 'Unpublished draft by %s in %s.', 112 + $blogger, 113 + $blog_link); 79 114 } else { 80 - $subtitle = pht('By %s on %s in %s.', $blogger, $date, $blog); 115 + $subtitle = pht( 116 + 'Written by %s on %s in %s.', 117 + $blogger, 118 + $date, 119 + $blog_link); 81 120 } 82 121 } else { 83 122 if ($post->isDraft()) { ··· 89 128 90 129 $item = id(new PHUIDocumentSummaryView()) 91 130 ->setTitle($post->getTitle()) 92 - ->setHref($post->getViewURI()) 131 + ->setHref($post_uri) 93 132 ->setSubtitle($subtitle) 94 133 ->setImage($blogger_image) 95 134 ->setImageHref($blogger_uri)
-130
src/applications/phame/view/PhamePostView.php
··· 1 - <?php 2 - 3 - final class PhamePostView extends AphrontView { 4 - 5 - private $post; 6 - private $author; 7 - private $body; 8 - private $skin; 9 - private $summary; 10 - 11 - 12 - public function setSkin(PhameBlogSkin $skin) { 13 - $this->skin = $skin; 14 - return $this; 15 - } 16 - 17 - public function getSkin() { 18 - return $this->skin; 19 - } 20 - 21 - public function setAuthor(PhabricatorObjectHandle $author) { 22 - $this->author = $author; 23 - return $this; 24 - } 25 - 26 - public function getAuthor() { 27 - return $this->author; 28 - } 29 - 30 - public function setPost(PhamePost $post) { 31 - $this->post = $post; 32 - return $this; 33 - } 34 - 35 - public function getPost() { 36 - return $this->post; 37 - } 38 - 39 - public function setBody($body) { 40 - $this->body = $body; 41 - return $this; 42 - } 43 - 44 - public function getBody() { 45 - return $this->body; 46 - } 47 - 48 - public function setSummary($summary) { 49 - $this->summary = $summary; 50 - return $this; 51 - } 52 - 53 - public function getSummary() { 54 - return $this->summary; 55 - } 56 - 57 - public function renderTitle() { 58 - $href = $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()); 59 - return phutil_tag( 60 - 'h2', 61 - array( 62 - 'class' => 'phame-post-title', 63 - ), 64 - phutil_tag( 65 - 'a', 66 - array( 67 - 'href' => $href, 68 - ), 69 - $this->getPost()->getTitle())); 70 - } 71 - 72 - public function renderDatePublished() { 73 - return phutil_tag( 74 - 'div', 75 - array( 76 - 'class' => 'phame-post-date', 77 - ), 78 - pht( 79 - 'Published on %s by %s', 80 - phabricator_datetime( 81 - $this->getPost()->getDatePublished(), 82 - $this->getUser()), 83 - $this->getAuthor()->getName())); 84 - } 85 - 86 - public function renderBody() { 87 - return phutil_tag( 88 - 'div', 89 - array( 90 - 'class' => 'phame-post-body phabricator-remarkup', 91 - ), 92 - $this->getBody()); 93 - } 94 - 95 - public function renderSummary() { 96 - return phutil_tag( 97 - 'div', 98 - array( 99 - 'class' => 'phame-post-body phabricator-remarkup', 100 - ), 101 - $this->getSummary()); 102 - } 103 - 104 - public function render() { 105 - return phutil_tag( 106 - 'div', 107 - array( 108 - 'class' => 'phame-post', 109 - ), 110 - array( 111 - $this->renderTitle(), 112 - $this->renderDatePublished(), 113 - $this->renderBody(), 114 - )); 115 - } 116 - 117 - public function renderWithSummary() { 118 - return phutil_tag( 119 - 'div', 120 - array( 121 - 'class' => 'phame-post', 122 - ), 123 - array( 124 - $this->renderTitle(), 125 - $this->renderDatePublished(), 126 - $this->renderSummary(), 127 - )); 128 - } 129 - 130 - }