Modernize Paste

Summary:
  - Add a PhabricatorApplication.
  - Make most of the views work well on tablets / phones. The actual "Create" form doesn't, but everything else is good -- need to make device-friendly form layouts before I can do the form.

Test Plan: Will attach screenshots.

Reviewers: btrahan, chad, vrana

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T1569

Differential Revision: https://secure.phabricator.com/D3293
This commit is contained in:
epriestley
2012-08-15 10:45:06 -07:00
parent 42461b5f06
commit 70a8e8b6e8
29 changed files with 1163 additions and 332 deletions

View File

@@ -65,8 +65,8 @@ celerity_register_resource_map(array(
),
'/rsrc/image/autosprite.png' =>
array(
'hash' => 'e3a103473dad6161a7be5369c2e21276',
'uri' => '/res/e3a10347/rsrc/image/autosprite.png',
'hash' => '08fb34fc4ce0cd3be882a3177423ad49',
'uri' => '/res/08fb34fc/rsrc/image/autosprite.png',
'disk' => '/rsrc/image/autosprite.png',
'type' => 'png',
),
@@ -643,7 +643,7 @@ celerity_register_resource_map(array(
),
'autosprite-css' =>
array(
'uri' => '/res/e09ec93c/rsrc/css/autosprite.css',
'uri' => '/res/07043fce/rsrc/css/autosprite.css',
'type' => 'css',
'requires' =>
array(
@@ -2215,6 +2215,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/js/application/herald/PathTypeahead.js',
),
'phabricator-action-list-view-css' =>
array(
'uri' => '/res/f70fbcd4/rsrc/css/layout/phabricator-action-list-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/layout/phabricator-action-list-view.css',
),
'phabricator-app-buttons-css' =>
array(
'uri' => '/res/1e153463/rsrc/css/application/directory/phabricator-app-buttons.css',
@@ -2262,7 +2271,7 @@ celerity_register_resource_map(array(
),
'phabricator-core-css' =>
array(
'uri' => '/res/f912ffab/rsrc/css/core/core.css',
'uri' => '/res/f3c6e0b5/rsrc/css/core/core.css',
'type' => 'css',
'requires' =>
array(
@@ -2334,6 +2343,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/flag/flag.css',
),
'phabricator-header-view-css' =>
array(
'uri' => '/res/c89cc14d/rsrc/css/layout/phabricator-header-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/layout/phabricator-header-view.css',
),
'phabricator-jump-nav' =>
array(
'uri' => '/res/8bdc0fc3/rsrc/css/application/directory/phabricator-jump-nav.css',
@@ -2429,6 +2447,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/base/notification-menu.css',
),
'phabricator-object-item-list-view-css' =>
array(
'uri' => '/res/7a31c016/rsrc/css/layout/phabricator-object-item-list-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/layout/phabricator-object-item-list-view.css',
),
'phabricator-object-list-view-css' =>
array(
'uri' => '/res/4f183668/rsrc/css/application/projects/phabricator-object-list-view.css',
@@ -2508,6 +2535,15 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/projects/project-tag.css',
),
'phabricator-property-list-view-css' =>
array(
'uri' => '/res/4a2b2d85/rsrc/css/layout/phabricator-property-list-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/layout/phabricator-property-list-view.css',
),
'phabricator-remarkup-css' =>
array(
'uri' => '/res/3b93a50d/rsrc/css/core/remarkup.css',
@@ -2547,9 +2583,18 @@ celerity_register_resource_map(array(
),
'disk' => '/rsrc/css/application/slowvote/slowvote.css',
),
'phabricator-source-code-view-css' =>
array(
'uri' => '/res/631aa90a/rsrc/css/layout/phabricator-source-code-view.css',
'type' => 'css',
'requires' =>
array(
),
'disk' => '/rsrc/css/layout/phabricator-source-code-view.css',
),
'phabricator-standard-page-view' =>
array(
'uri' => '/res/678262d4/rsrc/css/application/base/standard-page-view.css',
'uri' => '/res/13470ac6/rsrc/css/application/base/standard-page-view.css',
'type' => 'css',
'requires' =>
array(
@@ -2831,7 +2876,7 @@ celerity_register_resource_map(array(
), array(
'packages' =>
array(
'edf6b149' =>
'dd49ed34' =>
array(
'name' => 'core.pkg.css',
'symbols' =>
@@ -2860,7 +2905,7 @@ celerity_register_resource_map(array(
21 => 'phabricator-flag-css',
22 => 'aphront-error-view-css',
),
'uri' => '/res/pkg/edf6b149/core.pkg.css',
'uri' => '/res/pkg/dd49ed34/core.pkg.css',
'type' => 'css',
),
'971b021e' =>
@@ -3027,20 +3072,20 @@ celerity_register_resource_map(array(
'reverse' =>
array(
'aphront-attached-file-view-css' => '7839ae2d',
'aphront-crumbs-view-css' => 'edf6b149',
'aphront-dialog-view-css' => 'edf6b149',
'aphront-error-view-css' => 'edf6b149',
'aphront-form-view-css' => 'edf6b149',
'aphront-crumbs-view-css' => 'dd49ed34',
'aphront-dialog-view-css' => 'dd49ed34',
'aphront-error-view-css' => 'dd49ed34',
'aphront-form-view-css' => 'dd49ed34',
'aphront-headsup-action-list-view-css' => '19ebcc79',
'aphront-headsup-view-css' => 'edf6b149',
'aphront-list-filter-view-css' => 'edf6b149',
'aphront-pager-view-css' => 'edf6b149',
'aphront-panel-view-css' => 'edf6b149',
'aphront-side-nav-view-css' => 'edf6b149',
'aphront-table-view-css' => 'edf6b149',
'aphront-tokenizer-control-css' => 'edf6b149',
'aphront-tooltip-css' => 'edf6b149',
'aphront-typeahead-control-css' => 'edf6b149',
'aphront-headsup-view-css' => 'dd49ed34',
'aphront-list-filter-view-css' => 'dd49ed34',
'aphront-pager-view-css' => 'dd49ed34',
'aphront-panel-view-css' => 'dd49ed34',
'aphront-side-nav-view-css' => 'dd49ed34',
'aphront-table-view-css' => 'dd49ed34',
'aphront-tokenizer-control-css' => 'dd49ed34',
'aphront-tooltip-css' => 'dd49ed34',
'aphront-typeahead-control-css' => 'dd49ed34',
'differential-changeset-view-css' => '19ebcc79',
'differential-core-view-css' => '19ebcc79',
'differential-inline-comment-editor' => '26972e06',
@@ -3106,15 +3151,15 @@ celerity_register_resource_map(array(
'javelin-workflow' => '971b021e',
'maniphest-task-summary-css' => '7839ae2d',
'maniphest-transaction-detail-css' => '7839ae2d',
'phabricator-app-buttons-css' => 'edf6b149',
'phabricator-app-buttons-css' => 'dd49ed34',
'phabricator-content-source-view-css' => '19ebcc79',
'phabricator-core-buttons-css' => 'edf6b149',
'phabricator-core-css' => 'edf6b149',
'phabricator-directory-css' => 'edf6b149',
'phabricator-core-buttons-css' => 'dd49ed34',
'phabricator-core-css' => 'dd49ed34',
'phabricator-directory-css' => 'dd49ed34',
'phabricator-drag-and-drop-file-upload' => '26972e06',
'phabricator-dropdown-menu' => '971b021e',
'phabricator-flag-css' => 'edf6b149',
'phabricator-jump-nav' => 'edf6b149',
'phabricator-flag-css' => 'dd49ed34',
'phabricator-jump-nav' => 'dd49ed34',
'phabricator-keyboard-shortcut' => '971b021e',
'phabricator-keyboard-shortcut-manager' => '971b021e',
'phabricator-menu-item' => '971b021e',
@@ -3122,11 +3167,11 @@ celerity_register_resource_map(array(
'phabricator-paste-file-upload' => '971b021e',
'phabricator-prefab' => '971b021e',
'phabricator-project-tag-css' => '7839ae2d',
'phabricator-remarkup-css' => 'edf6b149',
'phabricator-remarkup-css' => 'dd49ed34',
'phabricator-shaped-request' => '26972e06',
'phabricator-standard-page-view' => 'edf6b149',
'phabricator-standard-page-view' => 'dd49ed34',
'phabricator-tooltip' => '971b021e',
'phabricator-transaction-view-css' => 'edf6b149',
'syntax-highlighting-css' => 'edf6b149',
'phabricator-transaction-view-css' => 'dd49ed34',
'syntax-highlighting-css' => 'dd49ed34',
),
));

View File

@@ -545,6 +545,8 @@ phutil_register_library_map(array(
'PackageModifyMail' => 'applications/owners/mail/PackageModifyMail.php',
'Phabricator404Controller' => 'applications/base/controller/Phabricator404Controller.php',
'PhabricatorAccessLog' => 'infrastructure/PhabricatorAccessLog.php',
'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php',
'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php',
'PhabricatorApplication' => 'applications/base/PhabricatorApplication.php',
'PhabricatorApplicationApplications' => 'applications/meta/application/PhabricatorApplicationApplications.php',
'PhabricatorApplicationAudit' => 'applications/audit/application/PhabricatorApplicationAudit.php',
@@ -559,6 +561,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationMailingLists' => 'applications/mailinglists/application/PhabricatorApplicationMailingLists.php',
'PhabricatorApplicationManiphest' => 'applications/maniphest/application/PhabricatorApplicationManiphest.php',
'PhabricatorApplicationMetaMTA' => 'applications/metamta/application/PhabricatorApplicationMetaMTA.php',
'PhabricatorApplicationPaste' => 'applications/paste/application/PhabricatorApplicationPaste.php',
'PhabricatorApplicationPeople' => 'applications/people/application/PhabricatorApplicationPeople.php',
'PhabricatorApplicationPhriction' => 'applications/phriction/application/PhabricatorApplicationPhriction.php',
'PhabricatorApplicationPonder' => 'applications/ponder/application/PhabricatorApplicationPonder.php',
@@ -747,6 +750,7 @@ phutil_register_library_map(array(
'PhabricatorGoodForNothingWorker' => 'infrastructure/daemon/workers/worker/PhabricatorGoodForNothingWorker.php',
'PhabricatorHandleObjectSelectorDataView' => 'applications/phid/handle/view/PhabricatorHandleObjectSelectorDataView.php',
'PhabricatorHash' => 'infrastructure/util/PhabricatorHash.php',
'PhabricatorHeaderView' => 'view/layout/PhabricatorHeaderView.php',
'PhabricatorHelpController' => 'applications/help/controller/PhabricatorHelpController.php',
'PhabricatorHelpKeyboardShortcutController' => 'applications/help/controller/PhabricatorHelpKeyboardShortcutController.php',
'PhabricatorIRCBot' => 'infrastructure/daemon/irc/PhabricatorIRCBot.php',
@@ -865,6 +869,8 @@ phutil_register_library_map(array(
'PhabricatorObjectHandleConstants' => 'applications/phid/handle/const/PhabricatorObjectHandleConstants.php',
'PhabricatorObjectHandleData' => 'applications/phid/handle/PhabricatorObjectHandleData.php',
'PhabricatorObjectHandleStatus' => 'applications/phid/handle/const/PhabricatorObjectHandleStatus.php',
'PhabricatorObjectItemListView' => 'view/layout/PhabricatorObjectItemListView.php',
'PhabricatorObjectItemView' => 'view/layout/PhabricatorObjectItemView.php',
'PhabricatorObjectListView' => 'view/control/PhabricatorObjectListView.php',
'PhabricatorObjectSelectorDialog' => 'view/control/PhabricatorObjectSelectorDialog.php',
'PhabricatorOffsetPagedQuery' => 'infrastructure/query/PhabricatorOffsetPagedQuery.php',
@@ -886,6 +892,7 @@ phutil_register_library_map(array(
'PhabricatorPaste' => 'applications/paste/storage/PhabricatorPaste.php',
'PhabricatorPasteController' => 'applications/paste/controller/PhabricatorPasteController.php',
'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php',
'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php',
'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php',
'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php',
'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php',
@@ -925,6 +932,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransaction' => 'applications/project/storage/PhabricatorProjectTransaction.php',
'PhabricatorProjectTransactionType' => 'applications/project/constants/PhabricatorProjectTransactionType.php',
'PhabricatorProjectUpdateController' => 'applications/project/controller/PhabricatorProjectUpdateController.php',
'PhabricatorPropertyListView' => 'view/layout/PhabricatorPropertyListView.php',
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
'PhabricatorRedirectController' => 'applications/base/controller/PhabricatorRedirectController.php',
'PhabricatorRefreshCSRFController' => 'applications/auth/controller/PhabricatorRefreshCSRFController.php',
@@ -1034,6 +1042,7 @@ phutil_register_library_map(array(
'PhabricatorSlug' => 'infrastructure/util/PhabricatorSlug.php',
'PhabricatorSlugTestCase' => 'infrastructure/util/__tests__/PhabricatorSlugTestCase.php',
'PhabricatorSortTableExample' => 'applications/uiexample/examples/PhabricatorSortTableExample.php',
'PhabricatorSourceCodeView' => 'view/layout/PhabricatorSourceCodeView.php',
'PhabricatorStandardPageView' => 'view/page/PhabricatorStandardPageView.php',
'PhabricatorStatusController' => 'applications/status/PhabricatorStatusController.php',
'PhabricatorStorageFixtureScopeGuard' => 'infrastructure/testing/fixture/PhabricatorStorageFixtureScopeGuard.php',
@@ -1673,6 +1682,8 @@ phutil_register_library_map(array(
'PackageDeleteMail' => 'PackageMail',
'PackageModifyMail' => 'PackageMail',
'Phabricator404Controller' => 'PhabricatorController',
'PhabricatorActionListView' => 'AphrontView',
'PhabricatorActionView' => 'AphrontView',
'PhabricatorApplicationApplications' => 'PhabricatorApplication',
'PhabricatorApplicationAudit' => 'PhabricatorApplication',
'PhabricatorApplicationAuth' => 'PhabricatorApplication',
@@ -1686,6 +1697,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationMailingLists' => 'PhabricatorApplication',
'PhabricatorApplicationManiphest' => 'PhabricatorApplication',
'PhabricatorApplicationMetaMTA' => 'PhabricatorApplication',
'PhabricatorApplicationPaste' => 'PhabricatorApplication',
'PhabricatorApplicationPeople' => 'PhabricatorApplication',
'PhabricatorApplicationPhriction' => 'PhabricatorApplication',
'PhabricatorApplicationPonder' => 'PhabricatorApplication',
@@ -1855,6 +1867,7 @@ phutil_register_library_map(array(
'PhabricatorGarbageCollectorDaemon' => 'PhabricatorDaemon',
'PhabricatorGlobalLock' => 'PhutilLock',
'PhabricatorGoodForNothingWorker' => 'PhabricatorWorker',
'PhabricatorHeaderView' => 'AphrontView',
'PhabricatorHelpController' => 'PhabricatorController',
'PhabricatorHelpKeyboardShortcutController' => 'PhabricatorHelpController',
'PhabricatorIRCBot' => 'PhabricatorDaemon',
@@ -1952,6 +1965,8 @@ phutil_register_library_map(array(
'PhabricatorOAuthServerTokenController' => 'PhabricatorAuthController',
'PhabricatorOAuthUnlinkController' => 'PhabricatorAuthController',
'PhabricatorObjectHandleStatus' => 'PhabricatorObjectHandleConstants',
'PhabricatorObjectItemListView' => 'AphrontView',
'PhabricatorObjectItemView' => 'AphrontView',
'PhabricatorObjectListView' => 'AphrontView',
'PhabricatorOffsetPagedQuery' => 'PhabricatorQuery',
'PhabricatorOwnersController' => 'PhabricatorController',
@@ -1977,6 +1992,7 @@ phutil_register_library_map(array(
),
'PhabricatorPasteController' => 'PhabricatorController',
'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
'PhabricatorPasteEditController' => 'PhabricatorPasteController',
'PhabricatorPasteListController' => 'PhabricatorPasteController',
'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyQuery',
'PhabricatorPasteViewController' => 'PhabricatorPasteController',
@@ -2014,6 +2030,7 @@ phutil_register_library_map(array(
'PhabricatorProjectTransaction' => 'PhabricatorProjectDAO',
'PhabricatorProjectTransactionType' => 'PhabricatorProjectConstants',
'PhabricatorProjectUpdateController' => 'PhabricatorProjectController',
'PhabricatorPropertyListView' => 'AphrontView',
'PhabricatorRedirectController' => 'PhabricatorController',
'PhabricatorRefreshCSRFController' => 'PhabricatorAuthController',
'PhabricatorRemarkupRuleCountdown' => 'PhutilRemarkupRule',
@@ -2108,6 +2125,7 @@ phutil_register_library_map(array(
'PhabricatorSlowvotePollController' => 'PhabricatorSlowvoteController',
'PhabricatorSlugTestCase' => 'PhabricatorTestCase',
'PhabricatorSortTableExample' => 'PhabricatorUIExample',
'PhabricatorSourceCodeView' => 'AphrontView',
'PhabricatorStandardPageView' => 'AphrontPageView',
'PhabricatorStatusController' => 'PhabricatorController',
'PhabricatorStorageManagementDatabasesWorkflow' => 'PhabricatorStorageManagementWorkflow',

View File

@@ -196,11 +196,6 @@ class AphrontDefaultApplicationConfiguration
'/status/' => 'PhabricatorStatusController',
'/paste/' => array(
'' => 'PhabricatorPasteListController',
'filter/(?P<filter>\w+)/' => 'PhabricatorPasteListController',
),
'/P(?P<id>\d+)' => 'PhabricatorPasteViewController',
'/help/' => array(
'keyboardshortcut/' => 'PhabricatorHelpKeyboardShortcutController',

View File

@@ -18,6 +18,8 @@
abstract class PhabricatorController extends AphrontController {
private $handles;
public function shouldRequireLogin() {
return true;
}
@@ -163,6 +165,11 @@ abstract class PhabricatorController extends AphrontController {
$page->appendChild($view);
if (idx($options, 'device')) {
$page->setDeviceReady(true);
$view->appendChild($page->renderFooter());
}
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
@@ -200,4 +207,19 @@ abstract class PhabricatorController extends AphrontController {
return $response;
}
protected function getHandle($phid) {
if (empty($this->handles[$phid])) {
throw new Exception(
"Attempting to access handle which wasn't loaded: {$phid}");
}
return $this->handles[$phid];
}
protected function loadHandles(array $phids) {
$phids = array_filter($phids);
$this->handles = id(new PhabricatorObjectHandleData($phids))
->setViewer($this->getRequest()->getUser())
->loadHandles();
return $this;
}
}

View File

@@ -0,0 +1,43 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorApplicationPaste extends PhabricatorApplication {
public function getBaseURI() {
return '/paste/';
}
public function getAutospriteName() {
return 'paste';
}
public function getTitleGlyph() {
return "\xE2\x9C\x8E";
}
public function getRoutes() {
return array(
'/P(?P<id>\d+)' => 'PhabricatorPasteViewController',
'/paste/' => array(
'' => 'PhabricatorPasteListController',
'filter/(?P<filter>\w+)/' => 'PhabricatorPasteListController',
),
);
}
}

View File

@@ -1,7 +1,7 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -18,6 +18,26 @@
abstract class PhabricatorPasteController extends PhabricatorController {
public function buildSideNavView(PhabricatorPaste $paste = null) {
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI('filter/')));
if ($paste) {
$nav->addFilter('paste', 'P'.$paste->getID(), '/P'.$paste->getID());
$nav->addSpacer();
}
$nav->addLabel('Create');
$nav->addFilter('create', 'New Paste');
$nav->addSpacer();
$nav->addLabel('Pastes');
$nav->addFilter('my', 'My Pastes');
$nav->addFilter('all', 'All Pastes');
return $nav;
}
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();

View File

@@ -102,31 +102,9 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
break;
}
$filters = array(
'create' => array(
'name' => 'Create Paste',
),
'my' => array(
'name' => 'My Pastes',
),
'all' => array(
'name' => 'All Pastes',
),
);
$side_nav = $this->buildSideNavView();
$side_nav->selectFilter($this->getFilter());
$side_nav = new AphrontSideNavView();
foreach ($filters as $filter_key => $filter) {
$selected = $filter_key == $this->getFilter();
$side_nav->addNavItem(
phutil_render_tag(
'a',
array(
'href' => '/paste/filter/'.$filter_key.'/',
'class' => $selected ? 'aphront-side-nav-selected': null,
),
$filter['name'])
);
}
if ($this->getErrorView()) {
$side_nav->appendChild($this->getErrorView());
@@ -142,7 +120,7 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
'href' => '/paste/filter/all',
),
'See all Pastes');
$header = "Recent Pastes &middot; {$see_all}";
$header = "Recent Pastes";
break;
case 'my':
$header = 'Your Pastes';
@@ -152,14 +130,17 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
break;
}
$this->loadHandles(mpull($paste_list, 'getAuthorPHID'));
$side_nav->appendChild(
$this->renderPasteList($paste_list, $header, $pager));
return $this->buildStandardPageResponse(
return $this->buildApplicationPage(
$side_nav,
array(
'title' => 'Paste',
'device' => true,
)
);
}
@@ -310,95 +291,26 @@ final class PhabricatorPasteListController extends PhabricatorPasteController {
private function renderPasteList(array $pastes, $header, $pager) {
assert_instances_of($pastes, 'PhabricatorPaste');
$phids = mpull($pastes, 'getAuthorPHID');
$handles = array();
if ($phids) {
$handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
}
$user = $this->getRequest()->getUser();
$phids = mpull($pastes, 'getFilePHID');
$file_uris = array();
if ($phids) {
$files = id(new PhabricatorFile())->loadAllWhere(
'phid in (%Ls)',
$phids);
if ($files) {
$file_uris = mpull($files, 'getBestURI', 'getPHID');
}
}
$paste_list_rows = array();
$list = new PhabricatorObjectItemListView();
$list->setHeader($header);
foreach ($pastes as $paste) {
$created = phabricator_datetime($paste->getDateCreated(), $user);
$handle = $handles[$paste->getAuthorPHID()];
$file_uri = $file_uris[$paste->getFilePHID()];
$item = id(new PhabricatorObjectItemView())
->setHeader('P'.$paste->getID().' '.$paste->getTitle())
->setHref('/P'.$paste->getID())
->addDetail(
pht('Author'),
$this->getHandle($paste->getAuthorPHID())->renderLink())
->addAttribute(pht('Created %s', $created));
$paste_list_rows[] = array(
phutil_escape_html('P'.$paste->getID()),
// TODO: Make this filter by user instead of going to their profile.
phutil_render_tag(
'a',
array(
'href' => '/p/'.$handle->getName().'/',
),
phutil_escape_html($handle->getName())),
phutil_escape_html($paste->getLanguage()),
phutil_render_tag(
'a',
array(
'href' => '/P'.$paste->getID(),
),
phutil_escape_html(
nonempty(
$paste->getTitle(),
'Untitled Masterwork P'.$paste->getID()))),
phutil_render_tag(
'a',
array(
'href' => $file_uri,
),
phutil_escape_html($paste->getFilePHID())),
phabricator_datetime(
$paste->getDateCreated(),
$this->getRequest()->getUser()),
);
$list->addItem($item);
}
$list->setPager($pager);
$table = new AphrontTableView($paste_list_rows);
$table->setHeaders(
array(
'Paste ID',
'Author',
'Language',
'Title',
'File',
'Created',
));
$table->setColumnClasses(
array(
null,
null,
null,
'wide pri',
null,
'right',
));
$panel = new AphrontPanelView();
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
$panel->setHeader($header);
$panel->appendChild($table);
if ($pager) {
$panel->appendChild($pager);
}
return $panel;
return $list;
}
}

View File

@@ -19,21 +19,20 @@
final class PhabricatorPasteViewController extends PhabricatorPasteController {
private $id;
private $handles;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$paste = id(new PhabricatorPasteQuery())
->setViewer($user)
->withPasteIDs(array($this->id))
->withIDs(array($this->id))
->executeOne();
if (!$paste) {
return new Aphront404Response();
}
@@ -45,94 +44,87 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
return new Aphront400Response();
}
$corpus = $this->buildCorpus($paste, $file);
$paste_panel = new AphrontPanelView();
$author_phid = $paste->getAuthorPHID();
$header = 'Viewing Paste '.$paste->getID().' by '.
PhabricatorObjectHandleData::loadOneHandle($author_phid)->renderLink();
if (strlen($paste->getTitle())) {
$header .= ' - '.phutil_escape_html($paste->getTitle());
}
$paste_panel->setHeader($header);
$paste_panel->setWidth(AphrontPanelView::WIDTH_FULL);
$paste_panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/paste/?fork='.$paste->getID(),
'class' => 'green button',
),
'Fork This'));
$raw_uri = $file->getBestURI();
$paste_panel->addButton(
phutil_render_tag(
'a',
array(
'href' => $raw_uri,
'class' => 'button',
),
'View Raw Text'));
$paste_panel->appendChild($corpus);
$forks_panel = null;
$forks_of_this_paste = id(new PhabricatorPaste())->loadAllWhere(
'parentPHID = %s',
$paste->getPHID());
if ($forks_of_this_paste) {
$forks_panel = new AphrontPanelView();
$forks_panel->setHeader("Forks of this paste");
$forks = array();
foreach ($forks_of_this_paste as $fork) {
$forks[] = array(
$fork->getID(),
phutil_render_tag(
'a',
array(
'href' => '/P'.$fork->getID(),
),
phutil_escape_html($fork->getTitle())
)
);
}
$forks_table = new AphrontTableView($forks);
$forks_table->setHeaders(
array(
'Paste ID',
'Title',
)
);
$forks_table->setColumnClasses(
array(
null,
'wide pri',
)
);
$forks_panel->appendChild($forks_table);
}
return $this->buildStandardPageResponse(
$this->loadHandles(
array(
$paste_panel,
$forks_panel,
),
$paste->getAuthorPHID(),
$paste->getParentPHID(),
));
$header = $this->buildHeaderView($paste);
$actions = $this->buildActionView($paste, $file);
$properties = $this->buildPropertyView($paste);
$source_code = $this->buildSourceCodeView($paste, $file);
$nav = $this->buildSideNavView($paste);
$nav->selectFilter('paste');
$nav->appendChild(
array(
'title' => 'Paste: '.nonempty($paste->getTitle(), 'P'.$paste->getID()),
$header,
$actions,
$properties,
$source_code,
// $forks_panel,
));
return $this->buildApplicationPage(
$nav,
array(
'title' => 'P'.$paste->getID().' '.$paste->getTitle(),
'device' => true,
));
}
private function buildCorpus($paste, $file) {
// Blantently copied from DiffusionBrowseFileController
private function buildHeaderView(PhabricatorPaste $paste) {
return id(new PhabricatorHeaderView())
->setObjectName('P'.$paste->getID())
->setHeader($paste->getTitle());
}
require_celerity_resource('diffusion-source-css');
require_celerity_resource('syntax-highlighting-css');
private function buildActionView(
PhabricatorPaste $paste,
PhabricatorFile $file) {
return id(new PhabricatorActionListView())
->addAction(
id(new PhabricatorActionView())
->setName(pht('Fork This Paste'))
->setIcon('fork')
->setHref($this->getApplicationURI('?fork='.$paste->getID())))
->addAction(
id(new PhabricatorActionView())
->setName(pht('View Raw File'))
->setIcon('file')
->setHref($file->getBestURI()));
}
private function buildPropertyView(PhabricatorPaste $paste) {
$user = $this->getRequest()->getUser();
$properties = new PhabricatorPropertyListView();
$properties->addProperty(
pht('Author'),
$this->getHandle($paste->getAuthorPHID())->renderLink());
$properties->addProperty(
pht('Created'),
phabricator_datetime($paste->getDateCreated(), $user));
if ($paste->getParentPHID()) {
$properties->addProperty(
pht('Forked From'),
$this->getHandle($paste->getParentPHID())->renderLink());
}
return $properties;
}
private function buildSourceCodeView(
PhabricatorPaste $paste,
PhabricatorFile $file) {
$language = $paste->getLanguage();
$source = $file->loadFileData();
if (empty($language)) {
$source = PhabricatorSyntaxHighlighter::highlightWithFilename(
$paste->getTitle(),
@@ -143,63 +135,10 @@ final class PhabricatorPasteViewController extends PhabricatorPasteController {
$source);
}
$text_list = explode("\n", $source);
$lines = explode("\n", $source);
Javelin::initBehavior('phabricator-oncopy', array());
$rows = $this->buildDisplayRows($text_list);
// TODO: Split the "one-up source listing" view into its own class and
// share it properly between Paste and Diffusion.
$corpus_table = phutil_render_tag(
'table',
array(
'class' => 'diffusion-source remarkup-code PhabricatorMonospaced',
),
implode("\n", $rows));
$corpus = phutil_render_tag(
'div',
array(
'style' => 'padding: 0pt 2em;',
),
$corpus_table);
return $corpus;
}
private function buildDisplayRows($text_list) {
$rows = array();
$n = 1;
foreach ($text_list as $k => $line) {
// Pardon the ugly for the time being.
// And eventually this will highlight a line that you click
// like diffusion does. Or maybe allow for line comments
// like differential. Either way it will be better than it is now.
$anchor = 'L'.$n;
$link = phutil_render_tag(
'a',
array(
'name' => $anchor,
'href' => '#'.$anchor,
),
$n);
$link = phutil_render_tag(
'th',
array(
'class' => 'diffusion-line-link',
),
$link);
$rows[] = '<tr id="'.$anchor.'">'.$link.
'<td style="white-space: pre-wrap;">'.
// NOTE: See the 'phabricator-oncopy' behavior.
"\xE2\x80\x8B".
$line.'</td></tr>';
++$n;
}
return $rows;
return id(new PhabricatorSourceCodeView())
->setLines($lines);
}
}

View File

@@ -18,11 +18,17 @@
final class PhabricatorPasteQuery extends PhabricatorCursorPagedPolicyQuery {
private $pasteIDs;
private $ids;
private $phids;
private $authorPHIDs;
public function withPasteIDs(array $ids) {
$this->pasteIDs = $ids;
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
@@ -53,11 +59,18 @@ final class PhabricatorPasteQuery extends PhabricatorCursorPagedPolicyQuery {
$where[] = $this->buildPagingClause($conn_r);
if ($this->pasteIDs) {
if ($this->ids) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ls)',
$this->pasteIDs);
'id IN (%Ld)',
$this->ids);
}
if ($this->phids) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
$this->phids);
}
if ($this->authorPHIDs) {

View File

@@ -19,11 +19,17 @@
final class PhabricatorObjectHandleData {
private $phids;
private $viewer;
public function __construct(array $phids) {
$this->phids = array_unique($phids);
}
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public static function loadOneHandle($phid) {
$handles = id(new PhabricatorObjectHandleData(array($phid)))->loadHandles();
return $handles[$phid];
@@ -443,7 +449,7 @@ final class PhabricatorObjectHandleData {
$questions = id(new PonderQuestionQuery())
->withPHIDs($phids)
->execute();
$questions = mpull($questions, 'getPHID');
$questions = mpull($questions, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
@@ -460,6 +466,29 @@ final class PhabricatorObjectHandleData {
$handles[$phid] = $handle;
}
break;
case PhabricatorPHIDConstants::PHID_TYPE_PSTE:
$pastes = id(new PhabricatorPasteQuery())
->withPHIDs($phids)
->setViewer($this->viewer)
->execute();
$pastes = mpull($pastes, null, 'getPHID');
foreach ($phids as $phid) {
$handle = new PhabricatorObjectHandle();
$handle->setPHID($phid);
$handle->setType($type);
if (empty($pastes[$phid])) {
$handle->setName('Unknown Paste');
} else {
$paste = $pastes[$phid];
$handle->setName($paste->getTitle());
$handle->setFullName('P'.$paste->getID().': '.$paste->getTitle());
$handle->setURI('/P'.$paste->getID());
$handle->setComplete(true);
}
$handles[$phid] = $handle;
}
break;
default:
$loader = null;
if (isset($external_loaders[$type])) {

View File

@@ -0,0 +1,41 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorActionListView extends AphrontView {
private $actions = array();
public function addAction(PhabricatorActionView $view) {
$this->actions[] = $view;
return $this;
}
public function render() {
require_celerity_resource('phabricator-action-list-view-css');
return phutil_render_tag(
'ul',
array(
'class' => 'phabricator-action-list-view',
),
$this->renderSingleView($this->actions));
}
}

View File

@@ -0,0 +1,79 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorActionView extends AphrontView {
private $name;
private $icon;
private $href;
public function setHref($href) {
$this->href = $href;
return $this;
}
public function setIcon($icon) {
$this->icon = $icon;
return $this;
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function render() {
$icon = null;
if ($this->icon) {
$icon = phutil_render_tag(
'span',
array(
'class' => 'phabricator-action-view-icon autosprite '.
'action-'.$this->icon,
),
'');
}
if ($this->href) {
$item = phutil_render_tag(
'a',
array(
'href' => $this->href,
'class' => 'phabricator-action-view-item',
),
phutil_escape_html($this->name));
} else {
$item = phutil_render_tag(
'span',
array(
'class' => 'phabricator-action-view-item',
),
phutil_escape_html($this->name));
}
return phutil_render_tag(
'li',
array(
'class' => 'phabricator-action-view',
),
$icon.$item);
}
}

View File

@@ -0,0 +1,57 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorHeaderView extends AphrontView {
private $objectName;
private $header;
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setObjectName($object_name) {
$this->objectName = $object_name;
return $this;
}
public function render() {
require_celerity_resource('phabricator-header-view-css');
$header = phutil_escape_html($this->header);
if ($this->objectName) {
$header = phutil_render_tag(
'a',
array(
'href' => '/'.$this->objectName,
),
phutil_escape_html($this->objectName)).' '.$header;
}
return phutil_render_tag(
'h1',
array(
'class' => 'phabricator-header-view',
),
$header);
}
}

View File

@@ -0,0 +1,64 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorObjectItemListView extends AphrontView {
private $header;
private $items;
private $pager;
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function setPager($pager) {
$this->pager = $pager;
return $this;
}
public function addItem(PhabricatorObjectItemView $item) {
$this->items[] = $item;
return $this;
}
public function render() {
require_celerity_resource('phabricator-object-item-list-view-css');
$header = phutil_render_tag(
'h1',
array(
'class' => 'phabricator-object-item-list-header',
),
phutil_escape_html($this->header));
$items = $this->renderSingleView($this->items);
$pager = null;
if ($this->pager) {
$pager = $this->renderSingleView($this->pager);
}
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-object-item-list-view',
),
$header.$items.$pager);
}
}

View File

@@ -0,0 +1,109 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorObjectItemView extends AphrontView {
private $header;
private $href;
private $details = array();
private $dates = array();
public function setHref($href) {
$this->href = $href;
return $this;
}
public function getHref() {
return $this->href;
}
public function setHeader($header) {
$this->header = $header;
return $this;
}
public function getHeader() {
return $this->header;
}
public function addDetail($name, $value, $class = null) {
$this->details[] = array(
'name' => $name,
'value' => $value,
);
return $this;
}
public function addAttribute($attribute) {
$this->attributes[] = $attribute;
return $this;
}
public function render() {
$header = phutil_render_tag(
'a',
array(
'href' => $this->href,
'class' => 'phabricator-object-item-name',
),
phutil_escape_html($this->header));
$details = null;
if ($this->details) {
$details = array();
foreach ($this->details as $detail) {
$details[] =
'<dt class="phabricator-object-detail-key">'.
phutil_escape_html($detail['name']).
'</dt>';
$details[] =
'<dd class="phabricator-object-detail-value">'.
$detail['value'].
'</dt>';
}
$details = phutil_render_tag(
'dl',
array(
'class' => 'phabricator-object-detail-list',
),
implode('', $details));
}
$attrs = null;
if ($this->attributes) {
$attrs = array();
foreach ($this->attributes as $attribute) {
$attrs[] = '<li>'.$attribute.'</li>';
}
$attrs = phutil_render_tag(
'ul',
array(
'class' => 'phabricator-object-item-attributes',
),
implode('', $attrs));
}
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-object-item',
),
$header.$details.$attrs);
}
}

View File

@@ -0,0 +1,62 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorPropertyListView extends AphrontView {
private $properties;
public function addProperty($key, $value) {
$this->properties[$key] = $value;
return $this;
}
public function render() {
require_celerity_resource('phabricator-property-list-view-css');
$items = array();
foreach ($this->properties as $key => $value) {
$items[] = phutil_render_tag(
'dt',
array(
'class' => 'phabricator-property-key',
),
phutil_escape_html($key));
$items[] = phutil_render_tag(
'dd',
array(
'class' => 'phabricator-property-value',
),
$this->renderSingleView($value));
}
$list = phutil_render_tag(
'dl',
array(
),
$this->renderSingleView($items));
return phutil_render_tag(
'div',
array(
'class' => 'phabricator-property-list-view',
),
$list);
}
}

View File

@@ -0,0 +1,68 @@
<?php
/*
* Copyright 2012 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorSourceCodeView extends AphrontView {
private $lines;
public function setLines(array $lines) {
$this->lines = $lines;
return $this;
}
public function render() {
require_celerity_resource('phabricator-source-code-view-css');
require_celerity_resource('syntax-highlighting-css');
Javelin::initBehavior('phabricator-oncopy', array());
$line_number = 1;
$rows = array();
foreach ($this->lines as $line) {
// TODO: Provide nice links.
$rows[] =
'<tr>'.
'<th class="phabricator-source-line">'.
phutil_escape_html($line_number).
'</th>'.
'<td class="phabricator-source-code">'.
"\xE2\x80\x8B".
$line.
'</td>'.
'</tr>';
$line_number++;
}
$classes = array();
$classes[] = 'phabricator-source-code-view';
$classes[] = 'remarkup-code';
$classes[] = 'PhabricatorMonospaced';
return phutil_render_tag(
'table',
array(
'class' => implode(' ', $classes),
),
implode('', $rows));
}
}

View File

@@ -31,6 +31,12 @@ final class PhabricatorStandardPageView extends AphrontPageView {
private $searchDefaultScope;
private $pageObjects = array();
private $controller;
private $deviceReady;
public function setDeviceReady($device_ready) {
$this->deviceReady = $device_ready;
return $this;
}
public function setController(AphrontController $controller) {
$this->controller = $controller;
@@ -203,12 +209,15 @@ final class PhabricatorStandardPageView extends AphrontPageView {
}
$viewport_tag = null;
if (PhabricatorEnv::getEnvConfig('preview.viewport-meta-tag')) {
if (PhabricatorEnv::getEnvConfig('preview.viewport-meta-tag') ||
$this->deviceReady) {
$viewport_tag = phutil_render_tag(
'meta',
array(
'name' => 'viewport',
'content' => 'width=device-width, initial-scale=1, maximum-scale=1',
'content' => 'width=device-width, '.
'initial-scale=1, '.
'maximum-scale=1',
));
}
@@ -293,40 +302,6 @@ final class PhabricatorStandardPageView extends AphrontPageView {
}
}
$foot_links = array();
$version = PhabricatorEnv::getEnvConfig('phabricator.version');
$foot_links[] = phutil_escape_html('Phabricator '.$version);
$foot_links[] =
'<a href="https://secure.phabricator.com/maniphest/task/create/">'.
'Report a Bug'.
'</a>';
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled') &&
!PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
if ($console) {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Disable DarkConsole');
} else {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Enable DarkConsole');
}
$foot_links[] = $link;
}
$foot_links = implode(' &middot; ', $foot_links);
$admin_class = null;
if ($this->getIsAdminInterface()) {
$admin_class = 'phabricator-admin-page-view';
@@ -336,10 +311,10 @@ final class PhabricatorStandardPageView extends AphrontPageView {
$footer_chrome = null;
if ($this->getShowChrome()) {
$header_chrome = $this->menuContent;
$footer_chrome =
'<div class="phabricator-page-foot">'.
$foot_links.
'</div>';
if (!$this->deviceReady) {
$footer_chrome = $this->renderFooter();
}
}
$developer_warning = null;
@@ -491,4 +466,47 @@ final class PhabricatorStandardPageView extends AphrontPageView {
return $menu->render();
}
public function renderFooter() {
$console = $this->getConsole();
$foot_links = array();
$version = PhabricatorEnv::getEnvConfig('phabricator.version');
$foot_links[] = phutil_escape_html('Phabricator '.$version);
$foot_links[] =
'<a href="https://secure.phabricator.com/maniphest/task/create/">'.
'Report a Bug'.
'</a>';
if (PhabricatorEnv::getEnvConfig('darkconsole.enabled') &&
!PhabricatorEnv::getEnvConfig('darkconsole.always-on')) {
if ($console) {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Disable DarkConsole');
} else {
$link = javelin_render_tag(
'a',
array(
'href' => '/~/',
'sigil' => 'workflow',
),
'Enable DarkConsole');
}
$foot_links[] = $link;
}
$foot_links = implode(' &middot; ', $foot_links);
return
'<div class="phabricator-page-foot">'.
$foot_links.
'</div>';
}
}

View File

@@ -16,7 +16,7 @@
* limitations under the License.
*/
function phabricator_date($epoch, $user) {
function phabricator_date($epoch, PhabricatorUser $user) {
return phabricator_format_local_time(
$epoch,
$user,