Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
index 41028fac61..1ee4a533a6 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
+++ b/src/applications/dashboard/controller/PhabricatorDashboardManageController.php
@@ -1,178 +1,175 @@
<?php
final class PhabricatorDashboardManageController
extends PhabricatorDashboardController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $this->id;
$dashboard_uri = $this->getApplicationURI('view/'.$id.'/');
// TODO: This UI should drop a lot of capabilities if the user can't
// edit the dashboard, but we should still let them in for "Install" and
// "View History".
$dashboard = id(new PhabricatorDashboardQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->needPanels(true)
->executeOne();
if (!$dashboard) {
return new Aphront404Response();
}
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$dashboard,
PhabricatorPolicyCapability::CAN_EDIT);
$title = $dashboard->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Dashboard %d', $dashboard->getID()),
$dashboard_uri);
$crumbs->addTextCrumb(pht('Manage'));
$header = $this->buildHeaderView($dashboard);
$actions = $this->buildActionView($dashboard);
$properties = $this->buildPropertyView($dashboard);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
if (!$can_edit) {
$no_edit = pht(
'You do not have permission to edit this dashboard. If you want to '.
'make changes, make a copy first.');
$box->setInfoView(
id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setErrors(array($no_edit)));
}
$rendered_dashboard = id(new PhabricatorDashboardRenderingEngine())
->setViewer($viewer)
->setDashboard($dashboard)
->setArrangeMode($can_edit)
->renderDashboard();
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$rendered_dashboard,
),
array(
'title' => $title,
));
}
private function buildHeaderView(PhabricatorDashboard $dashboard) {
$viewer = $this->getRequest()->getUser();
return id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($dashboard->getName())
->setPolicyObject($dashboard);
}
private function buildActionView(PhabricatorDashboard $dashboard) {
$viewer = $this->getRequest()->getUser();
$id = $dashboard->getID();
$actions = id(new PhabricatorActionListView())
->setObjectURI($this->getApplicationURI('view/'.$dashboard->getID().'/'))
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$dashboard,
PhabricatorPolicyCapability::CAN_EDIT);
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View Dashboard'))
->setIcon('fa-columns')
->setHref($this->getApplicationURI("view/{$id}/")));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Dashboard'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Copy Dashboard'))
->setIcon('fa-files-o')
->setHref($this->getApplicationURI("copy/{$id}/"))
->setWorkflow(true));
$installed_dashboard = id(new PhabricatorDashboardInstall())
->loadOneWhere(
'objectPHID = %s AND applicationClass = %s',
$viewer->getPHID(),
'PhabricatorHomeApplication');
if ($installed_dashboard &&
$installed_dashboard->getDashboardPHID() == $dashboard->getPHID()) {
$title_install = pht('Uninstall Dashboard');
$href_install = "uninstall/{$id}/";
} else {
$title_install = pht('Install Dashboard');
$href_install = "install/{$id}/";
}
$actions->addAction(
id(new PhabricatorActionView())
->setName($title_install)
->setIcon('fa-wrench')
->setHref($this->getApplicationURI($href_install))
->setWorkflow(true));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setIcon('fa-history')
->setHref($this->getApplicationURI("history/{$id}/")));
return $actions;
}
private function buildPropertyView(PhabricatorDashboard $dashboard) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($dashboard);
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$dashboard);
$properties->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
- $panel_phids = $dashboard->getPanelPHIDs();
- $this->loadHandles($panel_phids);
-
$properties->addProperty(
pht('Panels'),
- $this->renderHandlesForPHIDs($panel_phids));
+ $viewer->renderHandleList($dashboard->getPanelPHIDs()));
return $properties;
}
}
diff --git a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
index 91e664a9ae..01bd083f8f 100644
--- a/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
+++ b/src/applications/dashboard/controller/PhabricatorDashboardPanelViewController.php
@@ -1,180 +1,179 @@
<?php
final class PhabricatorDashboardPanelViewController
extends PhabricatorDashboardController {
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$panel = id(new PhabricatorDashboardPanelQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$panel) {
return new Aphront404Response();
}
$title = $panel->getMonogram().' '.$panel->getName();
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
pht('Panels'),
$this->getApplicationURI('panel/'));
$crumbs->addTextCrumb($panel->getMonogram());
$header = $this->buildHeaderView($panel);
$actions = $this->buildActionView($panel);
$properties = $this->buildPropertyView($panel);
$timeline = $this->buildTransactionTimeline(
$panel,
new PhabricatorDashboardPanelTransactionQuery());
$timeline->setShouldTerminate(true);
$properties->setActionList($actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$rendered_panel = id(new PhabricatorDashboardPanelRenderingEngine())
->setViewer($viewer)
->setPanel($panel)
->setParentPanelPHIDs(array())
->renderPanel();
$view = id(new PHUIBoxView())
->addMargin(PHUI::MARGIN_LARGE_LEFT)
->addMargin(PHUI::MARGIN_LARGE_RIGHT)
->addMargin(PHUI::MARGIN_LARGE_TOP)
->appendChild($rendered_panel);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$view,
$timeline,
),
array(
'title' => $title,
));
}
private function buildHeaderView(PhabricatorDashboardPanel $panel) {
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($panel->getName())
->setPolicyObject($panel);
if (!$panel->getIsArchived()) {
$header->setStatus('fa-check', 'bluegrey', pht('Active'));
} else {
$header->setStatus('fa-ban', 'red', pht('Archived'));
}
return $header;
}
private function buildActionView(PhabricatorDashboardPanel $panel) {
$viewer = $this->getRequest()->getUser();
$id = $panel->getID();
$actions = id(new PhabricatorActionListView())
->setObjectURI('/'.$panel->getMonogram())
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$panel,
PhabricatorPolicyCapability::CAN_EDIT);
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Panel'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("panel/edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if (!$panel->getIsArchived()) {
$archive_text = pht('Archive Panel');
$archive_icon = 'fa-ban';
} else {
$archive_text = pht('Activate Panel');
$archive_icon = 'fa-check';
}
$actions->addAction(
id(new PhabricatorActionView())
->setName($archive_text)
->setIcon($archive_icon)
->setHref($this->getApplicationURI("panel/archive/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View Standalone'))
->setIcon('fa-eye')
->setHref($this->getApplicationURI("panel/render/{$id}/")));
return $actions;
}
private function buildPropertyView(PhabricatorDashboardPanel $panel) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($panel);
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$panel);
$panel_type = $panel->getImplementation();
if ($panel_type) {
$type_name = $panel_type->getPanelTypeName();
} else {
$type_name = phutil_tag(
'em',
array(),
nonempty($panel->getPanelType(), pht('null')));
}
$properties->addProperty(
pht('Panel Type'),
$type_name);
$properties->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
$dashboard_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$panel->getPHID(),
PhabricatorDashboardPanelHasDashboardEdgeType::EDGECONST);
- $this->loadHandles($dashboard_phids);
$does_not_appear = pht(
'This panel does not appear on any dashboards.');
$properties->addProperty(
pht('Appears On'),
$dashboard_phids
- ? $this->renderHandlesForPHIDs($dashboard_phids)
+ ? $viewer->renderHandleList($dashboard_phids)
: phutil_tag('em', array(), $does_not_appear));
return $properties;
}
}
diff --git a/src/applications/files/controller/PhabricatorFileInfoController.php b/src/applications/files/controller/PhabricatorFileInfoController.php
index 27a67005d9..3d9731f6b4 100644
--- a/src/applications/files/controller/PhabricatorFileInfoController.php
+++ b/src/applications/files/controller/PhabricatorFileInfoController.php
@@ -1,377 +1,371 @@
<?php
final class PhabricatorFileInfoController extends PhabricatorFileController {
private $phid;
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->phid = idx($data, 'phid');
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($this->phid) {
$file = id(new PhabricatorFileQuery())
->setViewer($user)
->withPHIDs(array($this->phid))
->executeOne();
if (!$file) {
return new Aphront404Response();
}
return id(new AphrontRedirectResponse())->setURI($file->getInfoURI());
}
$file = id(new PhabricatorFileQuery())
->setViewer($user)
->withIDs(array($this->id))
->executeOne();
if (!$file) {
return new Aphront404Response();
}
$phid = $file->getPHID();
- $handle_phids = array_merge(
- array($file->getAuthorPHID()),
- $file->getObjectPHIDs());
-
- $this->loadHandles($handle_phids);
$header = id(new PHUIHeaderView())
->setUser($user)
->setPolicyObject($file)
->setHeader($file->getName());
$ttl = $file->getTTL();
if ($ttl !== null) {
$ttl_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_STATE)
->setBackgroundColor(PHUITagView::COLOR_YELLOW)
->setName(pht('Temporary'));
$header->addTag($ttl_tag);
}
$partial = $file->getIsPartial();
if ($partial) {
$partial_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_STATE)
->setBackgroundColor(PHUITagView::COLOR_ORANGE)
->setName(pht('Partial Upload'));
$header->addTag($partial_tag);
}
$actions = $this->buildActionView($file);
$timeline = $this->buildTransactionView($file);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(
'F'.$file->getID(),
$this->getApplicationURI("/info/{$phid}/"));
$object_box = id(new PHUIObjectBoxView())
->setHeader($header);
$this->buildPropertyViews($object_box, $file, $actions);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$timeline,
),
array(
'title' => $file->getName(),
'pageObjects' => array($file->getPHID()),
));
}
private function buildTransactionView(PhabricatorFile $file) {
$user = $this->getRequest()->getUser();
$timeline = $this->buildTransactionTimeline(
$file,
new PhabricatorFileTransactionQuery());
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('Question File Integrity');
$draft = PhabricatorDraft::newFromUserAndKey($user, $file->getPHID());
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setObjectPHID($file->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction($this->getApplicationURI('/comment/'.$file->getID().'/'))
->setSubmitButtonName(pht('Add Comment'));
return array(
$timeline,
$add_comment_form,
);
}
private function buildActionView(PhabricatorFile $file) {
$request = $this->getRequest();
$viewer = $request->getUser();
$id = $file->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$file,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($file);
$can_download = !$file->getIsPartial();
if ($file->isViewableInBrowser()) {
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View File'))
->setIcon('fa-file-o')
->setHref($file->getViewURI())
->setDisabled(!$can_download)
->setWorkflow(!$can_download));
} else {
$view->addAction(
id(new PhabricatorActionView())
->setUser($viewer)
->setRenderAsForm($can_download)
->setDownload($can_download)
->setName(pht('Download File'))
->setIcon('fa-download')
->setHref($file->getViewURI())
->setDisabled(!$can_download)
->setWorkflow(!$can_download));
}
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit File'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("/edit/{$id}/"))
->setWorkflow(!$can_edit)
->setDisabled(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete File'))
->setIcon('fa-times')
->setHref($this->getApplicationURI("/delete/{$id}/"))
->setWorkflow(true)
->setDisabled(!$can_edit));
return $view;
}
private function buildPropertyViews(
PHUIObjectBoxView $box,
PhabricatorFile $file,
PhabricatorActionListView $actions) {
$request = $this->getRequest();
$user = $request->getUser();
-
$properties = id(new PHUIPropertyListView());
$properties->setActionList($actions);
$box->addPropertyList($properties, pht('Details'));
if ($file->getAuthorPHID()) {
$properties->addProperty(
pht('Author'),
- $this->getHandle($file->getAuthorPHID())->renderLink());
+ $user->renderHandle($file->getAuthorPHID()));
}
$properties->addProperty(
pht('Created'),
phabricator_datetime($file->getDateCreated(), $user));
$finfo = id(new PHUIPropertyListView());
$box->addPropertyList($finfo, pht('File Info'));
$finfo->addProperty(
pht('Size'),
phutil_format_bytes($file->getByteSize()));
$finfo->addProperty(
pht('Mime Type'),
$file->getMimeType());
$width = $file->getImageWidth();
if ($width) {
$finfo->addProperty(
pht('Width'),
pht('%s px', new PhutilNumber($width)));
}
$height = $file->getImageHeight();
if ($height) {
$finfo->addProperty(
pht('Height'),
pht('%s px', new PhutilNumber($height)));
}
$is_image = $file->isViewableImage();
if ($is_image) {
$image_string = pht('Yes');
$cache_string = $file->getCanCDN() ? pht('Yes') : pht('No');
} else {
$image_string = pht('No');
$cache_string = pht('Not Applicable');
}
$finfo->addProperty(pht('Viewable Image'), $image_string);
$finfo->addProperty(pht('Cacheable'), $cache_string);
$builtin = $file->getBuiltinName();
if ($builtin === null) {
$builtin_string = pht('No');
} else {
$builtin_string = $builtin;
}
$finfo->addProperty(pht('Builtin'), $builtin_string);
$storage_properties = new PHUIPropertyListView();
$box->addPropertyList($storage_properties, pht('Storage'));
$storage_properties->addProperty(
pht('Engine'),
$file->getStorageEngine());
$storage_properties->addProperty(
pht('Format'),
$file->getStorageFormat());
$storage_properties->addProperty(
pht('Handle'),
$file->getStorageHandle());
$phids = $file->getObjectPHIDs();
if ($phids) {
$attached = new PHUIPropertyListView();
$box->addPropertyList($attached, pht('Attached'));
$attached->addProperty(
pht('Attached To'),
- $this->renderHandlesForPHIDs($phids));
+ $user->renderHandleList($phids));
}
if ($file->isViewableImage()) {
$image = phutil_tag(
'img',
array(
'src' => $file->getViewURI(),
'class' => 'phui-property-list-image',
));
$linked_image = phutil_tag(
'a',
array(
'href' => $file->getViewURI(),
),
$image);
$media = id(new PHUIPropertyListView())
->addImageContent($linked_image);
$box->addPropertyList($media);
} else if ($file->isAudio()) {
$audio = phutil_tag(
'audio',
array(
'controls' => 'controls',
'class' => 'phui-property-list-audio',
),
phutil_tag(
'source',
array(
'src' => $file->getViewURI(),
'type' => $file->getMimeType(),
)));
$media = id(new PHUIPropertyListView())
->addImageContent($audio);
$box->addPropertyList($media);
}
$engine = null;
try {
$engine = $file->instantiateStorageEngine();
} catch (Exception $ex) {
// Don't bother raising this anywhere for now.
}
if ($engine) {
if ($engine->isChunkEngine()) {
$chunkinfo = new PHUIPropertyListView();
$box->addPropertyList($chunkinfo, pht('Chunks'));
$chunks = id(new PhabricatorFileChunkQuery())
->setViewer($user)
->withChunkHandles(array($file->getStorageHandle()))
->execute();
$chunks = msort($chunks, 'getByteStart');
$rows = array();
$completed = array();
foreach ($chunks as $chunk) {
$is_complete = $chunk->getDataFilePHID();
$rows[] = array(
$chunk->getByteStart(),
$chunk->getByteEnd(),
($is_complete ? pht('Yes') : pht('No')),
);
if ($is_complete) {
$completed[] = $chunk;
}
}
$table = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Offset'),
pht('End'),
pht('Complete'),
))
->setColumnClasses(
array(
'',
'',
'wide',
));
$chunkinfo->addProperty(
pht('Total Chunks'),
count($chunks));
$chunkinfo->addProperty(
pht('Completed Chunks'),
count($completed));
$chunkinfo->addRawContent($table);
}
}
}
}
diff --git a/src/applications/passphrase/controller/PassphraseCredentialViewController.php b/src/applications/passphrase/controller/PassphraseCredentialViewController.php
index 8edd785a94..cabd721c89 100644
--- a/src/applications/passphrase/controller/PassphraseCredentialViewController.php
+++ b/src/applications/passphrase/controller/PassphraseCredentialViewController.php
@@ -1,214 +1,213 @@
<?php
final class PassphraseCredentialViewController extends PassphraseController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$credential = id(new PassphraseCredentialQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$credential) {
return new Aphront404Response();
}
$type = PassphraseCredentialType::getTypeByConstant(
$credential->getCredentialType());
if (!$type) {
throw new Exception(pht('Credential has invalid type "%s"!', $type));
}
$timeline = $this->buildTransactionTimeline(
$credential,
new PassphraseCredentialTransactionQuery());
$timeline->setShouldTerminate(true);
$title = pht('%s %s', 'K'.$credential->getID(), $credential->getName());
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb('K'.$credential->getID());
$header = $this->buildHeaderView($credential);
$actions = $this->buildActionView($credential, $type);
$properties = $this->buildPropertyView($credential, $type, $actions);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
),
array(
'title' => $title,
));
}
private function buildHeaderView(PassphraseCredential $credential) {
$viewer = $this->getRequest()->getUser();
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setHeader($credential->getName())
->setPolicyObject($credential);
if ($credential->getIsDestroyed()) {
$header->setStatus('fa-ban', 'red', pht('Destroyed'));
}
return $header;
}
private function buildActionView(
PassphraseCredential $credential,
PassphraseCredentialType $type) {
$viewer = $this->getRequest()->getUser();
$id = $credential->getID();
$is_locked = $credential->getIsLocked();
if ($is_locked) {
$credential_lock_text = pht('Locked Permanently');
$credential_lock_icon = 'fa-lock';
} else {
$credential_lock_text = pht('Lock Permanently');
$credential_lock_icon = 'fa-unlock';
}
$allow_conduit = $credential->getAllowConduit();
if ($allow_conduit) {
$credential_conduit_text = pht('Prevent Conduit Access');
$credential_conduit_icon = 'fa-ban';
} else {
$credential_conduit_text = pht('Allow Conduit Access');
$credential_conduit_icon = 'fa-wrench';
}
$actions = id(new PhabricatorActionListView())
->setObjectURI('/K'.$id)
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$credential,
PhabricatorPolicyCapability::CAN_EDIT);
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Credential'))
->setIcon('fa-pencil')
->setHref($this->getApplicationURI("edit/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
if (!$credential->getIsDestroyed()) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Destroy Credential'))
->setIcon('fa-times')
->setHref($this->getApplicationURI("destroy/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(true));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Show Secret'))
->setIcon('fa-eye')
->setHref($this->getApplicationURI("reveal/{$id}/"))
->setDisabled(!$can_edit || $is_locked)
->setWorkflow(true));
if ($type->hasPublicKey()) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Show Public Key'))
->setIcon('fa-download')
->setHref($this->getApplicationURI("public/{$id}/"))
->setWorkflow(true));
}
$actions->addAction(
id(new PhabricatorActionView())
->setName($credential_conduit_text)
->setIcon($credential_conduit_icon)
->setHref($this->getApplicationURI("conduit/{$id}/"))
->setWorkflow(true));
$actions->addAction(
id(new PhabricatorActionView())
->setName($credential_lock_text)
->setIcon($credential_lock_icon)
->setHref($this->getApplicationURI("lock/{$id}/"))
->setDisabled($is_locked)
->setWorkflow(true));
}
return $actions;
}
private function buildPropertyView(
PassphraseCredential $credential,
PassphraseCredentialType $type,
PhabricatorActionListView $actions) {
$viewer = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($credential)
->setActionList($actions);
$properties->addProperty(
pht('Credential Type'),
$type->getCredentialTypeName());
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
$viewer,
$credential);
$properties->addProperty(
pht('Editable By'),
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
$properties->addProperty(
pht('Username'),
$credential->getUsername());
$used_by_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$credential->getPHID(),
PhabricatorCredentialsUsedByObjectEdgeType::EDGECONST);
if ($used_by_phids) {
- $this->loadHandles($used_by_phids);
$properties->addProperty(
pht('Used By'),
- $this->renderHandlesForPHIDs($used_by_phids));
+ $viewer->renderHandleList($used_by_phids));
}
$description = $credential->getDescription();
if (strlen($description)) {
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addTextContent(
PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())
->setContent($description),
'default',
$viewer));
}
return $properties;
}
}
diff --git a/src/applications/paste/controller/PhabricatorPasteViewController.php b/src/applications/paste/controller/PhabricatorPasteViewController.php
index 92d150c3a7..0b18f94501 100644
--- a/src/applications/paste/controller/PhabricatorPasteViewController.php
+++ b/src/applications/paste/controller/PhabricatorPasteViewController.php
@@ -1,213 +1,213 @@
<?php
/**
* group paste
*/
final class PhabricatorPasteViewController extends PhabricatorPasteController {
private $id;
private $highlightMap;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
$raw_lines = idx($data, 'lines');
$map = array();
if ($raw_lines) {
$lines = explode('-', $raw_lines);
$first = idx($lines, 0, 0);
$last = idx($lines, 1);
if ($last) {
$min = min($first, $last);
$max = max($first, $last);
$map = array_fuse(range($min, $max));
} else {
$map[$first] = $first;
}
}
$this->highlightMap = $map;
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$paste = id(new PhabricatorPasteQuery())
->setViewer($user)
->withIDs(array($this->id))
->needContent(true)
->executeOne();
if (!$paste) {
return new Aphront404Response();
}
$file = id(new PhabricatorFileQuery())
->setViewer($user)
->withPHIDs(array($paste->getFilePHID()))
->executeOne();
if (!$file) {
return new Aphront400Response();
}
$forks = id(new PhabricatorPasteQuery())
->setViewer($user)
->withParentPHIDs(array($paste->getPHID()))
->execute();
$fork_phids = mpull($forks, 'getPHID');
$this->loadHandles(
array_merge(
array(
$paste->getAuthorPHID(),
$paste->getParentPHID(),
),
$fork_phids));
$header = $this->buildHeaderView($paste);
$actions = $this->buildActionView($user, $paste, $file);
$properties = $this->buildPropertyView($paste, $fork_phids, $actions);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$source_code = $this->buildSourceCodeView(
$paste,
null,
$this->highlightMap);
$source_code = id(new PHUIBoxView())
->appendChild($source_code)
->setBorder(true)
->addMargin(PHUI::MARGIN_LARGE_LEFT)
->addMargin(PHUI::MARGIN_LARGE_RIGHT)
->addMargin(PHUI::MARGIN_LARGE_TOP);
$crumbs = $this->buildApplicationCrumbs($this->buildSideNavView())
->addTextCrumb('P'.$paste->getID(), '/P'.$paste->getID());
$timeline = $this->buildTransactionTimeline(
$paste,
new PhabricatorPasteTransactionQuery());
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$add_comment_header = $is_serious
? pht('Add Comment')
: pht('Eat Paste');
$draft = PhabricatorDraft::newFromUserAndKey($user, $paste->getPHID());
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setObjectPHID($paste->getPHID())
->setDraft($draft)
->setHeaderText($add_comment_header)
->setAction($this->getApplicationURI('/comment/'.$paste->getID().'/'))
->setSubmitButtonName(pht('Add Comment'));
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$source_code,
$timeline,
$add_comment_form,
),
array(
'title' => $paste->getFullName(),
'pageObjects' => array($paste->getPHID()),
));
}
private function buildHeaderView(PhabricatorPaste $paste) {
$title = (nonempty($paste->getTitle())) ?
$paste->getTitle() : pht('(An Untitled Masterwork)');
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($this->getRequest()->getUser())
->setPolicyObject($paste);
return $header;
}
private function buildActionView(
PhabricatorUser $user,
PhabricatorPaste $paste,
PhabricatorFile $file) {
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$paste,
PhabricatorPolicyCapability::CAN_EDIT);
$can_fork = $user->isLoggedIn();
$fork_uri = $this->getApplicationURI('/create/?parent='.$paste->getID());
return id(new PhabricatorActionListView())
->setUser($user)
->setObject($paste)
->setObjectURI($this->getRequest()->getRequestURI())
->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Paste'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI('/edit/'.$paste->getID().'/')))
->addAction(
id(new PhabricatorActionView())
->setName(pht('Fork This Paste'))
->setIcon('fa-code-fork')
->setDisabled(!$can_fork)
->setWorkflow(!$can_fork)
->setHref($fork_uri))
->addAction(
id(new PhabricatorActionView())
->setName(pht('View Raw File'))
->setIcon('fa-file-text-o')
->setHref($file->getBestURI()));
}
private function buildPropertyView(
PhabricatorPaste $paste,
array $child_phids,
PhabricatorActionListView $actions) {
+ $viewer = $this->getViewer();
- $user = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
- ->setUser($user)
+ ->setUser($viewer)
->setObject($paste)
->setActionList($actions);
$properties->addProperty(
pht('Author'),
- $this->getHandle($paste->getAuthorPHID())->renderLink());
+ $viewer->renderHandle($paste->getAuthorPHID()));
$properties->addProperty(
pht('Created'),
- phabricator_datetime($paste->getDateCreated(), $user));
+ phabricator_datetime($paste->getDateCreated(), $viewer));
if ($paste->getParentPHID()) {
$properties->addProperty(
pht('Forked From'),
- $this->getHandle($paste->getParentPHID())->renderLink());
+ $viewer->renderHandle($paste->getParentPHID()));
}
if ($child_phids) {
$properties->addProperty(
pht('Forks'),
- $this->renderHandlesForPHIDs($child_phids));
+ $viewer->renderHandleList($child_phids));
}
$descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions(
- $user,
+ $viewer,
$paste);
return $properties;
}
}
diff --git a/src/applications/people/storage/PhabricatorUser.php b/src/applications/people/storage/PhabricatorUser.php
index 82548701f3..ea138042b2 100644
--- a/src/applications/people/storage/PhabricatorUser.php
+++ b/src/applications/people/storage/PhabricatorUser.php
@@ -1,962 +1,992 @@
<?php
/**
- * @task factors Multi-Factor Authentication
+ * @task factors Multi-Factor Authentication
+ * @task handles Managing Handles
*/
final class PhabricatorUser
extends PhabricatorUserDAO
implements
PhutilPerson,
PhabricatorPolicyInterface,
PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface,
PhabricatorSSHPublicKeyInterface {
const SESSION_TABLE = 'phabricator_session';
const NAMETOKEN_TABLE = 'user_nametoken';
const MAXIMUM_USERNAME_LENGTH = 64;
protected $userName;
protected $realName;
protected $sex;
protected $translation;
protected $passwordSalt;
protected $passwordHash;
protected $profileImagePHID;
protected $timezoneIdentifier = '';
protected $consoleEnabled = 0;
protected $consoleVisible = 0;
protected $consoleTab = '';
protected $conduitCertificate;
protected $isSystemAgent = 0;
protected $isAdmin = 0;
protected $isDisabled = 0;
protected $isEmailVerified = 0;
protected $isApproved = 0;
protected $isEnrolledInMultiFactor = 0;
protected $accountSecret;
private $profileImage = self::ATTACHABLE;
private $profile = null;
private $status = self::ATTACHABLE;
private $preferences = null;
private $omnipotent = false;
private $customFields = self::ATTACHABLE;
private $alternateCSRFString = self::ATTACHABLE;
private $session = self::ATTACHABLE;
private $authorities = array();
private $handlePool;
protected function readField($field) {
switch ($field) {
case 'timezoneIdentifier':
// If the user hasn't set one, guess the server's time.
return nonempty(
$this->timezoneIdentifier,
date_default_timezone_get());
// Make sure these return booleans.
case 'isAdmin':
return (bool)$this->isAdmin;
case 'isDisabled':
return (bool)$this->isDisabled;
case 'isSystemAgent':
return (bool)$this->isSystemAgent;
case 'isEmailVerified':
return (bool)$this->isEmailVerified;
case 'isApproved':
return (bool)$this->isApproved;
default:
return parent::readField($field);
}
}
/**
* Is this a live account which has passed required approvals? Returns true
* if this is an enabled, verified (if required), approved (if required)
* account, and false otherwise.
*
* @return bool True if this is a standard, usable account.
*/
public function isUserActivated() {
if ($this->isOmnipotent()) {
return true;
}
if ($this->getIsDisabled()) {
return false;
}
if (!$this->getIsApproved()) {
return false;
}
if (PhabricatorUserEmail::isEmailVerificationRequired()) {
if (!$this->getIsEmailVerified()) {
return false;
}
}
return true;
}
/**
* Returns `true` if this is a standard user who is logged in. Returns `false`
* for logged out, anonymous, or external users.
*
* @return bool `true` if the user is a standard user who is logged in with
* a normal session.
*/
public function getIsStandardUser() {
$type_user = PhabricatorPeopleUserPHIDType::TYPECONST;
return $this->getPHID() && (phid_get_type($this->getPHID()) == $type_user);
}
protected function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'userName' => 'sort64',
'realName' => 'text128',
'sex' => 'text4?',
'translation' => 'text64?',
'passwordSalt' => 'text32?',
'passwordHash' => 'text128?',
'profileImagePHID' => 'phid?',
'consoleEnabled' => 'bool',
'consoleVisible' => 'bool',
'consoleTab' => 'text64',
'conduitCertificate' => 'text255',
'isSystemAgent' => 'bool',
'isDisabled' => 'bool',
'isAdmin' => 'bool',
'timezoneIdentifier' => 'text255',
'isEmailVerified' => 'uint32',
'isApproved' => 'uint32',
'accountSecret' => 'bytes64',
'isEnrolledInMultiFactor' => 'bool',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'userName' => array(
'columns' => array('userName'),
'unique' => true,
),
'realName' => array(
'columns' => array('realName'),
),
'key_approved' => array(
'columns' => array('isApproved'),
),
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPeopleUserPHIDType::TYPECONST);
}
public function setPassword(PhutilOpaqueEnvelope $envelope) {
if (!$this->getPHID()) {
throw new Exception(
'You can not set a password for an unsaved user because their PHID '.
'is a salt component in the password hash.');
}
if (!strlen($envelope->openEnvelope())) {
$this->setPasswordHash('');
} else {
$this->setPasswordSalt(md5(Filesystem::readRandomBytes(32)));
$hash = $this->hashPassword($envelope);
$this->setPasswordHash($hash->openEnvelope());
}
return $this;
}
// To satisfy PhutilPerson.
public function getSex() {
return $this->sex;
}
public function getMonogram() {
return '@'.$this->getUsername();
}
public function isLoggedIn() {
return !($this->getPHID() === null);
}
public function save() {
if (!$this->getConduitCertificate()) {
$this->setConduitCertificate($this->generateConduitCertificate());
}
if (!strlen($this->getAccountSecret())) {
$this->setAccountSecret(Filesystem::readRandomCharacters(64));
}
$result = parent::save();
if ($this->profile) {
$this->profile->save();
}
$this->updateNameTokens();
id(new PhabricatorSearchIndexer())
->queueDocumentForIndexing($this->getPHID());
return $result;
}
public function attachSession(PhabricatorAuthSession $session) {
$this->session = $session;
return $this;
}
public function getSession() {
return $this->assertAttached($this->session);
}
public function hasSession() {
return ($this->session !== self::ATTACHABLE);
}
private function generateConduitCertificate() {
return Filesystem::readRandomCharacters(255);
}
public function comparePassword(PhutilOpaqueEnvelope $envelope) {
if (!strlen($envelope->openEnvelope())) {
return false;
}
if (!strlen($this->getPasswordHash())) {
return false;
}
return PhabricatorPasswordHasher::comparePassword(
$this->getPasswordHashInput($envelope),
new PhutilOpaqueEnvelope($this->getPasswordHash()));
}
private function getPasswordHashInput(PhutilOpaqueEnvelope $password) {
$input =
$this->getUsername().
$password->openEnvelope().
$this->getPHID().
$this->getPasswordSalt();
return new PhutilOpaqueEnvelope($input);
}
private function hashPassword(PhutilOpaqueEnvelope $password) {
$hasher = PhabricatorPasswordHasher::getBestHasher();
$input_envelope = $this->getPasswordHashInput($password);
return $hasher->getPasswordHashForStorage($input_envelope);
}
const CSRF_CYCLE_FREQUENCY = 3600;
const CSRF_SALT_LENGTH = 8;
const CSRF_TOKEN_LENGTH = 16;
const CSRF_BREACH_PREFIX = 'B@';
const EMAIL_CYCLE_FREQUENCY = 86400;
const EMAIL_TOKEN_LENGTH = 24;
private function getRawCSRFToken($offset = 0) {
return $this->generateToken(
time() + (self::CSRF_CYCLE_FREQUENCY * $offset),
self::CSRF_CYCLE_FREQUENCY,
PhabricatorEnv::getEnvConfig('phabricator.csrf-key'),
self::CSRF_TOKEN_LENGTH);
}
/**
* @phutil-external-symbol class PhabricatorStartup
*/
public function getCSRFToken() {
$salt = PhabricatorStartup::getGlobal('csrf.salt');
if (!$salt) {
$salt = Filesystem::readRandomCharacters(self::CSRF_SALT_LENGTH);
PhabricatorStartup::setGlobal('csrf.salt', $salt);
}
// Generate a token hash to mitigate BREACH attacks against SSL. See
// discussion in T3684.
$token = $this->getRawCSRFToken();
$hash = PhabricatorHash::digest($token, $salt);
return 'B@'.$salt.substr($hash, 0, self::CSRF_TOKEN_LENGTH);
}
public function validateCSRFToken($token) {
$salt = null;
$version = 'plain';
// This is a BREACH-mitigating token. See T3684.
$breach_prefix = self::CSRF_BREACH_PREFIX;
$breach_prelen = strlen($breach_prefix);
if (!strncmp($token, $breach_prefix, $breach_prelen)) {
$version = 'breach';
$salt = substr($token, $breach_prelen, self::CSRF_SALT_LENGTH);
$token = substr($token, $breach_prelen + self::CSRF_SALT_LENGTH);
}
// When the user posts a form, we check that it contains a valid CSRF token.
// Tokens cycle each hour (every CSRF_CYLCE_FREQUENCY seconds) and we accept
// either the current token, the next token (users can submit a "future"
// token if you have two web frontends that have some clock skew) or any of
// the last 6 tokens. This means that pages are valid for up to 7 hours.
// There is also some Javascript which periodically refreshes the CSRF
// tokens on each page, so theoretically pages should be valid indefinitely.
// However, this code may fail to run (if the user loses their internet
// connection, or there's a JS problem, or they don't have JS enabled).
// Choosing the size of the window in which we accept old CSRF tokens is
// an issue of balancing concerns between security and usability. We could
// choose a very narrow (e.g., 1-hour) window to reduce vulnerability to
// attacks using captured CSRF tokens, but it's also more likely that real
// users will be affected by this, e.g. if they close their laptop for an
// hour, open it back up, and try to submit a form before the CSRF refresh
// can kick in. Since the user experience of submitting a form with expired
// CSRF is often quite bad (you basically lose data, or it's a big pain to
// recover at least) and I believe we gain little additional protection
// by keeping the window very short (the overwhelming value here is in
// preventing blind attacks, and most attacks which can capture CSRF tokens
// can also just capture authentication information [sniffing networks]
// or act as the user [xss]) the 7 hour default seems like a reasonable
// balance. Other major platforms have much longer CSRF token lifetimes,
// like Rails (session duration) and Django (forever), which suggests this
// is a reasonable analysis.
$csrf_window = 6;
for ($ii = -$csrf_window; $ii <= 1; $ii++) {
$valid = $this->getRawCSRFToken($ii);
switch ($version) {
// TODO: We can remove this after the BREACH version has been in the
// wild for a while.
case 'plain':
if ($token == $valid) {
return true;
}
break;
case 'breach':
$digest = PhabricatorHash::digest($valid, $salt);
if (substr($digest, 0, self::CSRF_TOKEN_LENGTH) == $token) {
return true;
}
break;
default:
throw new Exception('Unknown CSRF token format!');
}
}
return false;
}
private function generateToken($epoch, $frequency, $key, $len) {
if ($this->getPHID()) {
$vec = $this->getPHID().$this->getAccountSecret();
} else {
$vec = $this->getAlternateCSRFString();
}
if ($this->hasSession()) {
$vec = $vec.$this->getSession()->getSessionKey();
}
$time_block = floor($epoch / $frequency);
$vec = $vec.$key.$time_block;
return substr(PhabricatorHash::digest($vec), 0, $len);
}
public function attachUserProfile(PhabricatorUserProfile $profile) {
$this->profile = $profile;
return $this;
}
public function loadUserProfile() {
if ($this->profile) {
return $this->profile;
}
$profile_dao = new PhabricatorUserProfile();
$this->profile = $profile_dao->loadOneWhere('userPHID = %s',
$this->getPHID());
if (!$this->profile) {
$profile_dao->setUserPHID($this->getPHID());
$this->profile = $profile_dao;
}
return $this->profile;
}
public function loadPrimaryEmailAddress() {
$email = $this->loadPrimaryEmail();
if (!$email) {
throw new Exception('User has no primary email address!');
}
return $email->getAddress();
}
public function loadPrimaryEmail() {
return $this->loadOneRelative(
new PhabricatorUserEmail(),
'userPHID',
'getPHID',
'(isPrimary = 1)');
}
public function loadPreferences() {
if ($this->preferences) {
return $this->preferences;
}
$preferences = null;
if ($this->getPHID()) {
$preferences = id(new PhabricatorUserPreferences())->loadOneWhere(
'userPHID = %s',
$this->getPHID());
}
if (!$preferences) {
$preferences = new PhabricatorUserPreferences();
$preferences->setUserPHID($this->getPHID());
$default_dict = array(
PhabricatorUserPreferences::PREFERENCE_TITLES => 'glyph',
PhabricatorUserPreferences::PREFERENCE_EDITOR => '',
PhabricatorUserPreferences::PREFERENCE_MONOSPACED => '',
PhabricatorUserPreferences::PREFERENCE_DARK_CONSOLE => 0,
);
$preferences->setPreferences($default_dict);
}
$this->preferences = $preferences;
return $preferences;
}
public function loadEditorLink($path, $line, $callsign) {
$editor = $this->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_EDITOR);
if (is_array($path)) {
$multiedit = $this->loadPreferences()->getPreference(
PhabricatorUserPreferences::PREFERENCE_MULTIEDIT);
switch ($multiedit) {
case '':
$path = implode(' ', $path);
break;
case 'disable':
return null;
}
}
if (!strlen($editor)) {
return null;
}
$uri = strtr($editor, array(
'%%' => '%',
'%f' => phutil_escape_uri($path),
'%l' => phutil_escape_uri($line),
'%r' => phutil_escape_uri($callsign),
));
// The resulting URI must have an allowed protocol. Otherwise, we'll return
// a link to an error page explaining the misconfiguration.
$ok = PhabricatorHelpEditorProtocolController::hasAllowedProtocol($uri);
if (!$ok) {
return '/help/editorprotocol/';
}
return (string)$uri;
}
public function getAlternateCSRFString() {
return $this->assertAttached($this->alternateCSRFString);
}
public function attachAlternateCSRFString($string) {
$this->alternateCSRFString = $string;
return $this;
}
/**
* Populate the nametoken table, which used to fetch typeahead results. When
* a user types "linc", we want to match "Abraham Lincoln" from on-demand
* typeahead sources. To do this, we need a separate table of name fragments.
*/
public function updateNameTokens() {
$table = self::NAMETOKEN_TABLE;
$conn_w = $this->establishConnection('w');
$tokens = PhabricatorTypeaheadDatasource::tokenizeString(
$this->getUserName().' '.$this->getRealName());
$sql = array();
foreach ($tokens as $token) {
$sql[] = qsprintf(
$conn_w,
'(%d, %s)',
$this->getID(),
$token);
}
queryfx(
$conn_w,
'DELETE FROM %T WHERE userID = %d',
$table,
$this->getID());
if ($sql) {
queryfx(
$conn_w,
'INSERT INTO %T (userID, token) VALUES %Q',
$table,
implode(', ', $sql));
}
}
public function sendWelcomeEmail(PhabricatorUser $admin) {
$admin_username = $admin->getUserName();
$admin_realname = $admin->getRealName();
$user_username = $this->getUserName();
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$base_uri = PhabricatorEnv::getProductionURI('/');
$engine = new PhabricatorAuthSessionEngine();
$uri = $engine->getOneTimeLoginURI(
$this,
$this->loadPrimaryEmail(),
PhabricatorAuthSessionEngine::ONETIME_WELCOME);
$body = <<<EOBODY
Welcome to Phabricator!
{$admin_username} ({$admin_realname}) has created an account for you.
Username: {$user_username}
To login to Phabricator, follow this link and set a password:
{$uri}
After you have set a password, you can login in the future by going here:
{$base_uri}
EOBODY;
if (!$is_serious) {
$body .= <<<EOBODY
Love,
Phabricator
EOBODY;
}
$mail = id(new PhabricatorMetaMTAMail())
->addTos(array($this->getPHID()))
->setForceDelivery(true)
->setSubject('[Phabricator] Welcome to Phabricator')
->setBody($body)
->saveAndSend();
}
public function sendUsernameChangeEmail(
PhabricatorUser $admin,
$old_username) {
$admin_username = $admin->getUserName();
$admin_realname = $admin->getRealName();
$new_username = $this->getUserName();
$password_instructions = null;
if (PhabricatorPasswordAuthProvider::getPasswordProvider()) {
$engine = new PhabricatorAuthSessionEngine();
$uri = $engine->getOneTimeLoginURI(
$this,
null,
PhabricatorAuthSessionEngine::ONETIME_USERNAME);
$password_instructions = <<<EOTXT
If you use a password to login, you'll need to reset it before you can login
again. You can reset your password by following this link:
{$uri}
And, of course, you'll need to use your new username to login from now on. If
you use OAuth to login, nothing should change.
EOTXT;
}
$body = <<<EOBODY
{$admin_username} ({$admin_realname}) has changed your Phabricator username.
Old Username: {$old_username}
New Username: {$new_username}
{$password_instructions}
EOBODY;
$mail = id(new PhabricatorMetaMTAMail())
->addTos(array($this->getPHID()))
->setForceDelivery(true)
->setSubject('[Phabricator] Username Changed')
->setBody($body)
->saveAndSend();
}
public static function describeValidUsername() {
return pht(
'Usernames must contain only numbers, letters, period, underscore and '.
'hyphen, and can not end with a period. They must have no more than %d '.
'characters.',
new PhutilNumber(self::MAXIMUM_USERNAME_LENGTH));
}
public static function validateUsername($username) {
// NOTE: If you update this, make sure to update:
//
// - Remarkup rule for @mentions.
// - Routing rule for "/p/username/".
// - Unit tests, obviously.
// - describeValidUsername() method, above.
if (strlen($username) > self::MAXIMUM_USERNAME_LENGTH) {
return false;
}
return (bool)preg_match('/^[a-zA-Z0-9._-]*[a-zA-Z0-9_-]\z/', $username);
}
public static function getDefaultProfileImageURI() {
return celerity_get_resource_uri('/rsrc/image/avatar.png');
}
public function attachStatus(PhabricatorCalendarEvent $status) {
$this->status = $status;
return $this;
}
public function getStatus() {
return $this->assertAttached($this->status);
}
public function hasStatus() {
return $this->status !== self::ATTACHABLE;
}
public function attachProfileImageURI($uri) {
$this->profileImage = $uri;
return $this;
}
public function getProfileImageURI() {
return $this->assertAttached($this->profileImage);
}
public function getFullName() {
if (strlen($this->getRealName())) {
return $this->getUsername().' ('.$this->getRealName().')';
} else {
return $this->getUsername();
}
}
public function __toString() {
return $this->getUsername();
}
public static function loadOneWithEmailAddress($address) {
$email = id(new PhabricatorUserEmail())->loadOneWhere(
'address = %s',
$address);
if (!$email) {
return null;
}
return id(new PhabricatorUser())->loadOneWhere(
'phid = %s',
$email->getUserPHID());
}
/**
* Grant a user a source of authority, to let them bypass policy checks they
* could not otherwise.
*/
public function grantAuthority($authority) {
$this->authorities[] = $authority;
return $this;
}
/**
* Get authorities granted to the user.
*/
public function getAuthorities() {
return $this->authorities;
}
/* -( Multi-Factor Authentication )---------------------------------------- */
/**
* Update the flag storing this user's enrollment in multi-factor auth.
*
* With certain settings, we need to check if a user has MFA on every page,
* so we cache MFA enrollment on the user object for performance. Calling this
* method synchronizes the cache by examining enrollment records. After
* updating the cache, use @{method:getIsEnrolledInMultiFactor} to check if
* the user is enrolled.
*
* This method should be called after any changes are made to a given user's
* multi-factor configuration.
*
* @return void
* @task factors
*/
public function updateMultiFactorEnrollment() {
$factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
$enrolled = count($factors) ? 1 : 0;
if ($enrolled !== $this->isEnrolledInMultiFactor) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
queryfx(
$this->establishConnection('w'),
'UPDATE %T SET isEnrolledInMultiFactor = %d WHERE id = %d',
$this->getTableName(),
$enrolled,
$this->getID());
unset($unguarded);
$this->isEnrolledInMultiFactor = $enrolled;
}
}
/**
* Check if the user is enrolled in multi-factor authentication.
*
* Enrolled users have one or more multi-factor authentication sources
* attached to their account. For performance, this value is cached. You
* can use @{method:updateMultiFactorEnrollment} to update the cache.
*
* @return bool True if the user is enrolled.
* @task factors
*/
public function getIsEnrolledInMultiFactor() {
return $this->isEnrolledInMultiFactor;
}
/* -( Omnipotence )-------------------------------------------------------- */
/**
* Returns true if this user is omnipotent. Omnipotent users bypass all policy
* checks.
*
* @return bool True if the user bypasses policy checks.
*/
public function isOmnipotent() {
return $this->omnipotent;
}
/**
* Get an omnipotent user object for use in contexts where there is no acting
* user, notably daemons.
*
* @return PhabricatorUser An omnipotent user.
*/
public static function getOmnipotentUser() {
static $user = null;
if (!$user) {
$user = new PhabricatorUser();
$user->omnipotent = true;
$user->makeEphemeral();
}
return $user;
}
-/* -( Handles )------------------------------------------------------------ */
+/* -( Managing Handles )--------------------------------------------------- */
/**
* Get a @{class:PhabricatorHandleList} which benefits from this viewer's
* internal handle pool.
*
* @param list<phid> List of PHIDs to load.
* @return PhabricatorHandleList Handle list object.
+ * @task handle
*/
public function loadHandles(array $phids) {
if ($this->handlePool === null) {
$this->handlePool = id(new PhabricatorHandlePool())
->setViewer($this);
}
return $this->handlePool->newHandleList($phids);
}
+ /**
+ * Get a @{class:PHUIHandleView} for a single handle.
+ *
+ * This benefits from the viewer's internal handle pool.
+ *
+ * @param phid PHID to render a handle for.
+ * @return PHUIHandleView View of the handle.
+ * @task handle
+ */
+ public function renderHandle($phid) {
+ return $this->loadHandles(array($phid))->renderHandle($phid);
+ }
+
+
+ /**
+ * Get a @{class:PHUIHandleListView} for a list of handles.
+ *
+ * This benefits from the viewer's internal handle pool.
+ *
+ * @param list<phid> List of PHIDs to render.
+ * @return PHUIHandleListView View of the handles.
+ * @task handle
+ */
+ public function renderHandleList(array $phids) {
+ return $this->loadHandles($phids)->renderList();
+ }
+
+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return PhabricatorPolicies::POLICY_PUBLIC;
case PhabricatorPolicyCapability::CAN_EDIT:
if ($this->getIsSystemAgent()) {
return PhabricatorPolicies::POLICY_ADMIN;
} else {
return PhabricatorPolicies::POLICY_NOONE;
}
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
return $this->getPHID() && ($viewer->getPHID() === $this->getPHID());
}
public function describeAutomaticCapability($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_EDIT:
return pht('Only you can edit your information.');
default:
return null;
}
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('user.fields');
}
public function getCustomFieldBaseClass() {
return 'PhabricatorUserCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$this->delete();
$externals = id(new PhabricatorExternalAccount())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
foreach ($externals as $external) {
$external->delete();
}
$prefs = id(new PhabricatorUserPreferences())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
foreach ($prefs as $pref) {
$pref->delete();
}
$profiles = id(new PhabricatorUserProfile())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
foreach ($profiles as $profile) {
$profile->delete();
}
$keys = id(new PhabricatorAuthSSHKey())->loadAllWhere(
'objectPHID = %s',
$this->getPHID());
foreach ($keys as $key) {
$key->delete();
}
$emails = id(new PhabricatorUserEmail())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
foreach ($emails as $email) {
$email->delete();
}
$sessions = id(new PhabricatorAuthSession())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
foreach ($sessions as $session) {
$session->delete();
}
$factors = id(new PhabricatorAuthFactorConfig())->loadAllWhere(
'userPHID = %s',
$this->getPHID());
foreach ($factors as $factor) {
$factor->delete();
}
$this->saveTransaction();
}
/* -( PhabricatorSSHPublicKeyInterface )----------------------------------- */
public function getSSHPublicKeyManagementURI(PhabricatorUser $viewer) {
if ($viewer->getPHID() == $this->getPHID()) {
// If the viewer is managing their own keys, take them to the normal
// panel.
return '/settings/panel/ssh/';
} else {
// Otherwise, take them to the administrative panel for this user.
return '/settings/'.$this->getID().'/panel/ssh/';
}
}
public function getSSHKeyDefaultName() {
return 'id_rsa_phabricator';
}
}
diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php
index 3f67817ccf..3487a21dbf 100644
--- a/src/applications/pholio/controller/PholioMockViewController.php
+++ b/src/applications/pholio/controller/PholioMockViewController.php
@@ -1,217 +1,215 @@
<?php
final class PholioMockViewController extends PholioController {
private $id;
private $imageID;
private $maniphestTaskPHIDs = array();
private function setManiphestTaskPHIDs($maniphest_task_phids) {
$this->maniphestTaskPHIDs = $maniphest_task_phids;
return $this;
}
private function getManiphestTaskPHIDs() {
return $this->maniphestTaskPHIDs;
}
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = $data['id'];
$this->imageID = idx($data, 'imageID');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$mock = id(new PholioMockQuery())
->setViewer($user)
->withIDs(array($this->id))
->needImages(true)
->needInlineComments(true)
->executeOne();
if (!$mock) {
return new Aphront404Response();
}
$phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
$mock->getPHID(),
PholioMockHasTaskEdgeType::EDGECONST);
$this->setManiphestTaskPHIDs($phids);
- $phids[] = $mock->getAuthorPHID();
- $this->loadHandles($phids);
$engine = id(new PhabricatorMarkupEngine())
->setViewer($user);
$engine->addObject($mock, PholioMock::MARKUP_FIELD_DESCRIPTION);
$title = $mock->getName();
if ($mock->isClosed()) {
$header_icon = 'fa-ban';
$header_name = pht('Closed');
$header_color = 'dark';
} else {
$header_icon = 'fa-square-o';
$header_name = pht('Open');
$header_color = 'bluegrey';
}
$header = id(new PHUIHeaderView())
->setHeader($title)
->setUser($user)
->setStatus($header_icon, $header_color, $header_name)
->setPolicyObject($mock);
$timeline = $this->buildTransactionTimeline(
$mock,
new PholioTransactionQuery(),
$engine);
$timeline->setMock($mock);
$actions = $this->buildActionView($mock);
$properties = $this->buildPropertyView($mock, $engine, $actions);
require_celerity_resource('pholio-css');
require_celerity_resource('pholio-inline-comments-css');
$comment_form_id = celerity_generate_unique_node_id();
$output = id(new PholioMockImagesView())
->setRequestURI($request->getRequestURI())
->setCommentFormID($comment_form_id)
->setUser($user)
->setMock($mock)
->setImageID($this->imageID);
$output = id(new PHUIObjectBoxView())
->setHeaderText(pht('Image'))
->appendChild($output);
$add_comment = $this->buildAddCommentView($mock, $comment_form_id);
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb('M'.$mock->getID(), '/M'.$mock->getID());
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
$thumb_grid = id(new PholioMockThumbGridView())
->setUser($user)
->setMock($mock);
$content = array(
$crumbs,
$object_box,
$output,
$thumb_grid,
$timeline,
$add_comment,
);
return $this->buildApplicationPage(
$content,
array(
'title' => 'M'.$mock->getID().' '.$title,
'pageObjects' => array($mock->getPHID()),
));
}
private function buildActionView(PholioMock $mock) {
$user = $this->getRequest()->getUser();
$actions = id(new PhabricatorActionListView())
->setUser($user)
->setObjectURI($this->getRequest()->getRequestURI())
->setObject($mock);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$user,
$mock,
PhabricatorPolicyCapability::CAN_EDIT);
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-pencil')
->setName(pht('Edit Mock'))
->setHref($this->getApplicationURI('/edit/'.$mock->getID().'/'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$actions->addAction(
id(new PhabricatorActionView())
->setIcon('fa-anchor')
->setName(pht('Edit Maniphest Tasks'))
->setHref("/search/attach/{$mock->getPHID()}/TASK/edge/")
->setDisabled(!$user->isLoggedIn())
->setWorkflow(true));
return $actions;
}
private function buildPropertyView(
PholioMock $mock,
PhabricatorMarkupEngine $engine,
PhabricatorActionListView $actions) {
$user = $this->getRequest()->getUser();
$properties = id(new PHUIPropertyListView())
->setUser($user)
->setObject($mock)
->setActionList($actions);
$properties->addProperty(
pht('Author'),
- $this->getHandle($mock->getAuthorPHID())->renderLink());
+ $user->renderHandle($mock->getAuthorPHID()));
$properties->addProperty(
pht('Created'),
phabricator_datetime($mock->getDateCreated(), $user));
if ($this->getManiphestTaskPHIDs()) {
$properties->addProperty(
pht('Maniphest Tasks'),
- $this->renderHandlesForPHIDs($this->getManiphestTaskPHIDs()));
+ $user->renderHandleList($this->getManiphestTaskPHIDs()));
}
$properties->invokeWillRenderEvent();
$properties->addSectionHeader(
pht('Description'),
PHUIPropertyListView::ICON_SUMMARY);
$properties->addImageContent(
$engine->getOutput($mock, PholioMock::MARKUP_FIELD_DESCRIPTION));
return $properties;
}
private function buildAddCommentView(PholioMock $mock, $comment_form_id) {
$user = $this->getRequest()->getUser();
$draft = PhabricatorDraft::newFromUserAndKey($user, $mock->getPHID());
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
$title = $is_serious
? pht('Add Comment')
: pht('History Beckons');
$form = id(new PhabricatorApplicationTransactionCommentView())
->setUser($user)
->setObjectPHID($mock->getPHID())
->setFormID($comment_form_id)
->setDraft($draft)
->setHeaderText($title)
->setSubmitButtonName(pht('Add Comment'))
->setAction($this->getApplicationURI('/comment/'.$mock->getID().'/'))
->setRequestURI($this->getRequest()->getRequestURI());
return $form;
}
}
diff --git a/src/applications/phortune/controller/PhortuneAccountListController.php b/src/applications/phortune/controller/PhortuneAccountListController.php
index 89fdf58b9d..8c7d765b9d 100644
--- a/src/applications/phortune/controller/PhortuneAccountListController.php
+++ b/src/applications/phortune/controller/PhortuneAccountListController.php
@@ -1,109 +1,107 @@
<?php
final class PhortuneAccountListController extends PhortuneController {
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$accounts = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withMemberPHIDs(array($viewer->getPHID()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
$merchants = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withMemberPHIDs(array($viewer->getPHID()))
->execute();
$title = pht('Accounts');
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb(pht('Accounts'));
$payment_list = id(new PHUIObjectItemListView())
->setStackable(true)
->setUser($viewer)
->setNoDataString(
pht(
'You are not a member of any payment accounts. Payment '.
'accounts are used to make purchases.'));
foreach ($accounts as $account) {
$this->loadHandles($account->getMemberPHIDs());
- $members = $this->renderHandlesForPHIDs($account->getMemberPHIDs(), ',');
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Account %d', $account->getID()))
->setHeader($account->getName())
->setHref($this->getApplicationURI($account->getID().'/'))
- ->addAttribute(pht('Members: %s', $members))
->setObject($account);
$payment_list->addItem($item);
}
$payment_header = id(new PHUIHeaderView())
->setHeader(pht('Payment Accounts'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($this->getApplicationURI('account/edit/'))
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-plus'))
->setText(pht('Create Account')));
$payment_box = id(new PHUIObjectBoxView())
->setHeader($payment_header)
->appendChild($payment_list);
$merchant_list = id(new PHUIObjectItemListView())
->setStackable(true)
->setUser($viewer)
->setNoDataString(
pht(
'You do not control any merchant accounts. Merchant accounts are '.
'used to receive payments.'));
foreach ($merchants as $merchant) {
$item = id(new PHUIObjectItemView())
->setObjectName(pht('Merchant %d', $merchant->getID()))
->setHeader($merchant->getName())
->setHref($this->getApplicationURI('/merchant/'.$merchant->getID().'/'))
->setObject($merchant);
$merchant_list->addItem($item);
}
$merchant_header = id(new PHUIHeaderView())
->setHeader(pht('Merchant Accounts'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setHref($this->getApplicationURI('merchant/'))
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-list'))
->setText(pht('View All Merchants')));
$merchant_box = id(new PHUIObjectBoxView())
->setHeader($merchant_header)
->appendChild($merchant_list);
return $this->buildApplicationPage(
array(
$crumbs,
$payment_box,
$merchant_box,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/phortune/controller/PhortuneAccountViewController.php b/src/applications/phortune/controller/PhortuneAccountViewController.php
index 2a50c2ef0f..7991a260b3 100644
--- a/src/applications/phortune/controller/PhortuneAccountViewController.php
+++ b/src/applications/phortune/controller/PhortuneAccountViewController.php
@@ -1,392 +1,386 @@
<?php
final class PhortuneAccountViewController extends PhortuneController {
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
// TODO: Currently, you must be able to edit an account to view the detail
// page, because the account must be broadly visible so merchants can
// process orders but merchants should not be able to see all the details
// of an account. Ideally this page should be visible to merchants, too,
// just with less information.
$can_edit = true;
$account = id(new PhortuneAccountQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('accountID')))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->executeOne();
if (!$account) {
return new Aphront404Response();
}
$title = $account->getName();
$invoices = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withInvoices(true)
->execute();
$crumbs = $this->buildApplicationCrumbs();
$this->addAccountCrumb($crumbs, $account, $link = false);
$header = id(new PHUIHeaderView())
->setHeader($title);
$edit_uri = $this->getApplicationURI('account/edit/'.$account->getID().'/');
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObjectURI($request->getRequestURI())
->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Account'))
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$properties = id(new PHUIPropertyListView())
->setObject($account)
->setUser($viewer);
- $this->loadHandles($account->getMemberPHIDs());
-
$properties->addProperty(
pht('Members'),
- $this->renderHandlesForPHIDs($account->getMemberPHIDs()));
+ $viewer->renderHandleList($account->getMemberPHIDs()));
$status_items = $this->getStatusItemsForAccount($account, $invoices);
$status_view = new PHUIStatusListView();
foreach ($status_items as $item) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(
idx($item, 'icon'),
idx($item, 'color'),
idx($item, 'label'))
->setTarget(idx($item, 'target'))
->setNote(idx($item, 'note')));
}
$properties->addProperty(
pht('Status'),
$status_view);
$properties->setActionList($actions);
$invoices = $this->buildInvoicesSection($account, $invoices);
$purchase_history = $this->buildPurchaseHistorySection($account);
$charge_history = $this->buildChargeHistorySection($account);
$subscriptions = $this->buildSubscriptionsSection($account);
$payment_methods = $this->buildPaymentMethodsSection($account);
$timeline = $this->buildTransactionTimeline(
$account,
new PhortuneAccountTransactionQuery());
$timeline->setShouldTerminate(true);
$object_box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
return $this->buildApplicationPage(
array(
$crumbs,
$object_box,
$invoices,
$purchase_history,
$charge_history,
$subscriptions,
$payment_methods,
$timeline,
),
array(
'title' => $title,
));
}
private function buildPaymentMethodsSection(PhortuneAccount $account) {
$request = $this->getRequest();
$viewer = $request->getUser();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$account,
PhabricatorPolicyCapability::CAN_EDIT);
$id = $account->getID();
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Methods'));
$list = id(new PHUIObjectItemListView())
->setUser($viewer)
->setFlush(true)
->setNoDataString(
pht('No payment methods associated with this account.'));
$methods = id(new PhortunePaymentMethodQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->execute();
- if ($methods) {
- $this->loadHandles(mpull($methods, 'getAuthorPHID'));
- }
-
foreach ($methods as $method) {
$id = $method->getID();
$item = new PHUIObjectItemView();
$item->setHeader($method->getFullDisplayName());
switch ($method->getStatus()) {
case PhortunePaymentMethod::STATUS_ACTIVE:
$item->setBarColor('green');
$disable_uri = $this->getApplicationURI('card/'.$id.'/disable/');
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-times')
->setHref($disable_uri)
->setDisabled(!$can_edit)
->setWorkflow(true));
break;
case PhortunePaymentMethod::STATUS_DISABLED:
$item->setDisabled(true);
break;
}
$provider = $method->buildPaymentProvider();
$item->addAttribute($provider->getPaymentMethodProviderDescription());
$edit_uri = $this->getApplicationURI('card/'.$id.'/edit/');
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pencil')
->setHref($edit_uri)
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$list->addItem($item);
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($list);
}
private function buildInvoicesSection(
PhortuneAccount $account,
array $carts) {
$request = $this->getRequest();
$viewer = $request->getUser();
$phids = array();
foreach ($carts as $cart) {
$phids[] = $cart->getPHID();
$phids[] = $cart->getMerchantPHID();
foreach ($cart->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$table = id(new PhortuneOrderTableView())
->setNoDataString(pht('You have no unpaid invoices.'))
->setIsInvoices(true)
->setUser($viewer)
->setCarts($carts)
->setHandles($handles);
$header = id(new PHUIHeaderView())
->setHeader(pht('Invoices Due'));
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
}
private function buildPurchaseHistorySection(PhortuneAccount $account) {
$request = $this->getRequest();
$viewer = $request->getUser();
$carts = id(new PhortuneCartQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needPurchases(true)
->withStatuses(
array(
PhortuneCart::STATUS_PURCHASING,
PhortuneCart::STATUS_CHARGED,
PhortuneCart::STATUS_HOLD,
PhortuneCart::STATUS_REVIEW,
PhortuneCart::STATUS_PURCHASED,
))
->setLimit(10)
->execute();
$phids = array();
foreach ($carts as $cart) {
$phids[] = $cart->getPHID();
foreach ($cart->getPurchases() as $purchase) {
$phids[] = $purchase->getPHID();
}
}
$handles = $this->loadViewerHandles($phids);
$orders_uri = $this->getApplicationURI($account->getID().'/order/');
$table = id(new PhortuneOrderTableView())
->setUser($viewer)
->setCarts($carts)
->setHandles($handles);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Orders'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-list'))
->setHref($orders_uri)
->setText(pht('View All Orders')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
}
private function buildChargeHistorySection(PhortuneAccount $account) {
$request = $this->getRequest();
$viewer = $request->getUser();
$charges = id(new PhortuneChargeQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->needCarts(true)
->setLimit(10)
->execute();
$phids = array();
foreach ($charges as $charge) {
$phids[] = $charge->getProviderPHID();
$phids[] = $charge->getCartPHID();
$phids[] = $charge->getMerchantPHID();
$phids[] = $charge->getPaymentMethodPHID();
}
$handles = $this->loadViewerHandles($phids);
$charges_uri = $this->getApplicationURI($account->getID().'/charge/');
$table = id(new PhortuneChargeTableView())
->setUser($viewer)
->setCharges($charges)
->setHandles($handles);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Charges'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-list'))
->setHref($charges_uri)
->setText(pht('View All Charges')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
}
private function buildSubscriptionsSection(PhortuneAccount $account) {
$request = $this->getRequest();
$viewer = $request->getUser();
$subscriptions = id(new PhortuneSubscriptionQuery())
->setViewer($viewer)
->withAccountPHIDs(array($account->getPHID()))
->setLimit(10)
->execute();
$subscriptions_uri = $this->getApplicationURI(
$account->getID().'/subscription/');
$handles = $this->loadViewerHandles(mpull($subscriptions, 'getPHID'));
$table = id(new PhortuneSubscriptionTableView())
->setUser($viewer)
->setHandles($handles)
->setSubscriptions($subscriptions);
$header = id(new PHUIHeaderView())
->setHeader(pht('Recent Subscriptions'))
->addActionLink(
id(new PHUIButtonView())
->setTag('a')
->setIcon(
id(new PHUIIconView())
->setIconFont('fa-list'))
->setHref($subscriptions_uri)
->setText(pht('View All Subscriptions')));
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($table);
}
protected function buildApplicationCrumbs() {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->addAction(
id(new PHUIListItemView())
->setIcon('fa-exchange')
->setHref($this->getApplicationURI('account/'))
->setName(pht('Switch Accounts')));
return $crumbs;
}
private function getStatusItemsForAccount(
PhortuneAccount $account,
array $invoices) {
assert_instances_of($invoices, 'PhortuneCart');
$items = array();
if ($invoices) {
$items[] = array(
'icon' => PHUIStatusItemView::ICON_WARNING,
'color' => 'yellow',
'target' => pht('Invoices'),
'note' => pht('You have %d unpaid invoice(s).', count($invoices)),
);
} else {
$items[] = array(
'icon' => PHUIStatusItemView::ICON_ACCEPT,
'color' => 'green',
'target' => pht('Invoices'),
'note' => pht('This account has no unpaid invoices.'),
);
}
// TODO: If a payment method has expired or is expiring soon, we should
// add a status check for it.
return $items;
}
}
diff --git a/src/applications/phortune/controller/PhortuneMerchantViewController.php b/src/applications/phortune/controller/PhortuneMerchantViewController.php
index 675a1afc47..94a53742b0 100644
--- a/src/applications/phortune/controller/PhortuneMerchantViewController.php
+++ b/src/applications/phortune/controller/PhortuneMerchantViewController.php
@@ -1,294 +1,292 @@
<?php
final class PhortuneMerchantViewController
extends PhortuneMerchantController {
private $id;
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$merchant = id(new PhortuneMerchantQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if (!$merchant) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb($merchant->getName());
$title = pht(
'Merchant %d %s',
$merchant->getID(),
$merchant->getName());
$header = id(new PHUIHeaderView())
->setObjectName(pht('Merchant %d', $merchant->getID()))
->setHeader($merchant->getName())
->setUser($viewer)
->setPolicyObject($merchant);
$providers = id(new PhortunePaymentProviderConfigQuery())
->setViewer($viewer)
->withMerchantPHIDs(array($merchant->getPHID()))
->execute();
$properties = $this->buildPropertyListView($merchant, $providers);
$actions = $this->buildActionListView($merchant);
$properties->setActionList($actions);
$provider_list = $this->buildProviderList(
$merchant,
$providers);
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($properties);
$timeline = $this->buildTransactionTimeline(
$merchant,
new PhortuneMerchantTransactionQuery());
$timeline->setShouldTerminate(true);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$provider_list,
$timeline,
),
array(
'title' => $title,
));
}
private function buildPropertyListView(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($merchant);
$status_view = new PHUIStatusListView();
$have_any = false;
$any_test = false;
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
if ($provider->isEnabled()) {
$have_any = true;
}
if (!$provider->isAcceptingLivePayments()) {
$any_test = true;
}
}
if ($have_any) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Accepts Payments'))
->setNote(pht('This merchant can accept payments.')));
if ($any_test) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('Test Mode'))
->setNote(pht('This merchant is accepting test payments.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_ACCEPT, 'green')
->setTarget(pht('Live Mode'))
->setNote(pht('This merchant is accepting live payments.')));
}
} else if ($providers) {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_REJECT, 'red')
->setTarget(pht('No Enabled Providers'))
->setNote(
pht(
'All of the payment providers for this merchant are '.
'disabled.')));
} else {
$status_view->addItem(
id(new PHUIStatusItemView())
->setIcon(PHUIStatusItemView::ICON_WARNING, 'yellow')
->setTarget(pht('No Providers'))
->setNote(
pht(
'This merchant does not have any payment providers configured '.
'yet, so it can not accept payments. Add a provider.')));
}
$view->addProperty(pht('Status'), $status_view);
- $this->loadHandles($merchant->getMemberPHIDs());
-
$view->addProperty(
pht('Members'),
- $this->renderHandlesForPHIDs($merchant->getMemberPHIDs()));
+ $viewer->renderHandleList($merchant->getMemberPHIDs()));
$view->invokeWillRenderEvent();
$description = $merchant->getDescription();
if (strlen($description)) {
$description = PhabricatorMarkupEngine::renderOneObject(
id(new PhabricatorMarkupOneOff())->setContent($description),
'default',
$viewer);
$view->addSectionHeader(pht('Description'));
$view->addTextContent($description);
}
return $view;
}
private function buildActionListView(PhortuneMerchant $merchant) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($merchant);
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Merchant'))
->setIcon('fa-pencil')
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setHref($this->getApplicationURI("merchant/edit/{$id}/")));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View Orders'))
->setIcon('fa-shopping-cart')
->setHref($this->getApplicationURI("merchant/orders/{$id}/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
$view->addAction(
id(new PhabricatorActionView())
->setName(pht('View Subscriptions'))
->setIcon('fa-moon-o')
->setHref($this->getApplicationURI("merchant/{$id}/subscription/"))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit));
return $view;
}
private function buildProviderList(
PhortuneMerchant $merchant,
array $providers) {
$viewer = $this->getRequest()->getUser();
$id = $merchant->getID();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$merchant,
PhabricatorPolicyCapability::CAN_EDIT);
$provider_list = id(new PHUIObjectItemListView())
->setFlush(true)
->setNoDataString(pht('This merchant has no payment providers.'));
foreach ($providers as $provider_config) {
$provider = $provider_config->buildProvider();
$provider_id = $provider_config->getID();
$item = id(new PHUIObjectItemView())
->setHeader($provider->getName());
if ($provider->isEnabled()) {
if ($provider->isAcceptingLivePayments()) {
$item->setBarColor('green');
} else {
$item->setBarColor('yellow');
$item->addIcon('fa-exclamation-triangle', pht('Test Mode'));
}
$item->addAttribute($provider->getConfigureProvidesDescription());
} else {
// Don't show disabled providers to users who can't manage the merchant
// account.
if (!$can_edit) {
continue;
}
$item->setDisabled(true);
$item->addAttribute(
phutil_tag('em', array(), pht('This payment provider is disabled.')));
}
if ($can_edit) {
$edit_uri = $this->getApplicationURI(
"/provider/edit/{$provider_id}/");
$disable_uri = $this->getApplicationURI(
"/provider/disable/{$provider_id}/");
if ($provider->isEnabled()) {
$disable_icon = 'fa-times';
$disable_name = pht('Disable');
} else {
$disable_icon = 'fa-check';
$disable_name = pht('Enable');
}
$item->addAction(
id(new PHUIListItemView())
->setIcon($disable_icon)
->setHref($disable_uri)
->setName($disable_name)
->setWorkflow(true));
$item->addAction(
id(new PHUIListItemView())
->setIcon('fa-pencil')
->setHref($edit_uri)
->setName(pht('Edit')));
}
$provider_list->addItem($item);
}
$add_action = id(new PHUIButtonView())
->setTag('a')
->setHref($this->getApplicationURI('provider/edit/?merchantID='.$id))
->setText(pht('Add Payment Provider'))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon(id(new PHUIIconView())->setIconFont('fa-plus'));
$header = id(new PHUIHeaderView())
->setHeader(pht('Payment Providers'))
->addActionLink($add_action);
return id(new PHUIObjectBoxView())
->setHeader($header)
->appendChild($provider_list);
}
}
diff --git a/src/applications/phragment/controller/PhragmentController.php b/src/applications/phragment/controller/PhragmentController.php
index 00ca5aa566..096ee21a1f 100644
--- a/src/applications/phragment/controller/PhragmentController.php
+++ b/src/applications/phragment/controller/PhragmentController.php
@@ -1,233 +1,227 @@
<?php
abstract class PhragmentController extends PhabricatorController {
protected function loadParentFragments($path) {
$components = explode('/', $path);
$combinations = array();
$current = '';
foreach ($components as $component) {
$current .= '/'.$component;
$current = trim($current, '/');
if (trim($current) === '') {
continue;
}
$combinations[] = $current;
}
$fragments = array();
$results = id(new PhragmentFragmentQuery())
->setViewer($this->getRequest()->getUser())
->needLatestVersion(true)
->withPaths($combinations)
->execute();
foreach ($combinations as $combination) {
$found = false;
foreach ($results as $fragment) {
if ($fragment->getPath() === $combination) {
$fragments[] = $fragment;
$found = true;
break;
}
}
if (!$found) {
return null;
}
}
return $fragments;
}
protected function buildApplicationCrumbsWithPath(array $fragments) {
$crumbs = $this->buildApplicationCrumbs();
$crumbs->addTextCrumb('/', '/phragment/');
foreach ($fragments as $parent) {
$crumbs->addTextCrumb(
$parent->getName(),
'/phragment/browse/'.$parent->getPath());
}
return $crumbs;
}
protected function createCurrentFragmentView($fragment, $is_history_view) {
if ($fragment === null) {
return null;
}
$viewer = $this->getRequest()->getUser();
- $phids = array();
- $phids[] = $fragment->getLatestVersionPHID();
-
$snapshot_phids = array();
$snapshots = id(new PhragmentSnapshotQuery())
->setViewer($viewer)
->withPrimaryFragmentPHIDs(array($fragment->getPHID()))
->execute();
foreach ($snapshots as $snapshot) {
- $phids[] = $snapshot->getPHID();
$snapshot_phids[] = $snapshot->getPHID();
}
- $this->loadHandles($phids);
-
$file = null;
$file_uri = null;
if (!$fragment->isDirectory()) {
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($fragment->getLatestVersion()->getFilePHID()))
->executeOne();
if ($file !== null) {
$file_uri = $file->getDownloadURI();
}
}
$header = id(new PHUIHeaderView())
->setHeader($fragment->getName())
->setPolicyObject($fragment)
->setUser($viewer);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$fragment,
PhabricatorPolicyCapability::CAN_EDIT);
$zip_uri = $this->getApplicationURI('zip/'.$fragment->getPath());
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($fragment)
->setObjectURI($fragment->getURI());
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Fragment'))
->setHref($this->isCorrectlyConfigured() ? $file_uri : null)
->setDisabled($file === null || !$this->isCorrectlyConfigured())
->setIcon('fa-download'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Contents as ZIP'))
->setHref($this->isCorrectlyConfigured() ? $zip_uri : null)
->setDisabled(!$this->isCorrectlyConfigured())
->setIcon('fa-floppy-o'));
if (!$fragment->isDirectory()) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Update Fragment'))
->setHref($this->getApplicationURI('update/'.$fragment->getPath()))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-refresh'));
} else {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Convert to File'))
->setHref($this->getApplicationURI('update/'.$fragment->getPath()))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-file-o'));
}
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Set Fragment Policies'))
->setHref($this->getApplicationURI('policy/'.$fragment->getPath()))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-asterisk'));
if ($is_history_view) {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View Child Fragments'))
->setHref($this->getApplicationURI('browse/'.$fragment->getPath()))
->setIcon('fa-search-plus'));
} else {
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setHref($this->getApplicationURI('history/'.$fragment->getPath()))
->setIcon('fa-list'));
}
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Create Snapshot'))
->setHref($this->getApplicationURI(
'snapshot/create/'.$fragment->getPath()))
->setDisabled(!$can_edit)
->setWorkflow(!$can_edit)
->setIcon('fa-files-o'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Promote Snapshot to Here'))
->setHref($this->getApplicationURI(
'snapshot/promote/latest/'.$fragment->getPath()))
->setWorkflow(true)
->setDisabled(!$can_edit)
->setIcon('fa-arrow-circle-up'));
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($fragment)
->setActionList($actions);
if (!$fragment->isDirectory()) {
if ($fragment->isDeleted()) {
$properties->addProperty(
pht('Type'),
pht('File (Deleted)'));
} else {
$properties->addProperty(
pht('Type'),
pht('File'));
}
$properties->addProperty(
pht('Latest Version'),
- $this->renderHandlesForPHIDs(array($fragment->getLatestVersionPHID())));
+ $viewer->renderHandle($fragment->getLatestVersionPHID()));
} else {
$properties->addProperty(
pht('Type'),
pht('Directory'));
}
if (count($snapshot_phids) > 0) {
$properties->addProperty(
pht('Snapshots'),
- $this->renderHandlesForPHIDs($snapshot_phids));
+ $viewer->renderHandleList($snapshot_phids));
}
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
}
public function renderConfigurationWarningIfRequired() {
$alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
if ($alt === null) {
return id(new PHUIInfoView())
->setTitle(pht('security.alternate-file-domain must be configured!'))
->setSeverity(PHUIInfoView::SEVERITY_ERROR)
->appendChild(phutil_tag('p', array(), pht(
'Because Phragment generates files (such as ZIP archives and '.
'patches) as they are requested, it requires that you configure '.
'the `security.alternate-file-domain` option. This option on it\'s '.
'own will also provide additional security when serving files '.
'across Phabricator.')));
}
return null;
}
/**
* We use this to disable the download links if the alternate domain is
* not configured correctly. Although the download links will mostly work
* for logged in users without an alternate domain, the behaviour is
* reasonably non-consistent and will deny public users, even if policies
* are configured otherwise (because the Files app does not support showing
* the info page to viewers who are not logged in).
*/
public function isCorrectlyConfigured() {
$alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
return $alt !== null;
}
}
diff --git a/src/applications/phragment/controller/PhragmentCreateController.php b/src/applications/phragment/controller/PhragmentCreateController.php
index 5c9aee5207..d899754a1c 100644
--- a/src/applications/phragment/controller/PhragmentCreateController.php
+++ b/src/applications/phragment/controller/PhragmentCreateController.php
@@ -1,132 +1,138 @@
<?php
final class PhragmentCreateController extends PhragmentController {
private $dblob;
public function willProcessRequest(array $data) {
$this->dblob = idx($data, 'dblob', '');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$parent = null;
$parents = $this->loadParentFragments($this->dblob);
if ($parents === null) {
return new Aphront404Response();
}
if (count($parents) !== 0) {
$parent = idx($parents, count($parents) - 1, null);
}
$parent_path = '';
if ($parent !== null) {
$parent_path = $parent->getPath();
}
$parent_path = trim($parent_path, '/');
$fragment = id(new PhragmentFragment());
$error_view = null;
if ($request->isFormPost()) {
$errors = array();
$v_name = $request->getStr('name');
$v_fileid = $request->getInt('fileID');
$v_viewpolicy = $request->getStr('viewPolicy');
$v_editpolicy = $request->getStr('editPolicy');
if (strpos($v_name, '/') !== false) {
$errors[] = pht('The fragment name can not contain \'/\'.');
}
- $file = id(new PhabricatorFile())->load($v_fileid);
- if ($file === null) {
+ $file = id(new PhabricatorFileQuery())
+ ->setViewer($viewer)
+ ->withIDs(array($v_fileid))
+ ->executeOne();
+ if (!$file) {
$errors[] = pht('The specified file doesn\'t exist.');
}
if (!count($errors)) {
$depth = 1;
if ($parent !== null) {
$depth = $parent->getDepth() + 1;
}
PhragmentFragment::createFromFile(
$viewer,
$file,
trim($parent_path.'/'.$v_name, '/'),
$v_viewpolicy,
$v_editpolicy);
return id(new AphrontRedirectResponse())
->setURI('/phragment/browse/'.trim($parent_path.'/'.$v_name, '/'));
} else {
$error_view = id(new PHUIInfoView())
->setErrors($errors)
->setTitle(pht('Errors while creating fragment'));
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($viewer)
->setObject($fragment)
->execute();
$form = id(new AphrontFormView())
->setUser($viewer)
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Parent Path'))
->setDisabled(true)
->setValue('/'.trim($parent_path.'/', '/')))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Name'))
->setName('name'))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('File ID'))
->setName('fileID'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($viewer)
->setName('viewPolicy')
->setPolicyObject($fragment)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($viewer)
->setName('editPolicy')
->setPolicyObject($fragment)
->setPolicies($policies)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue(pht('Create Fragment'))
->addCancelButton(
$this->getApplicationURI('browse/'.$parent_path)));
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
$crumbs->addTextCrumb(pht('Create Fragment'));
$box = id(new PHUIObjectBoxView())
->setHeaderText('Create Fragment')
- ->setValidationException(null)
->setForm($form);
+ if ($error_view) {
+ $box->setInfoView($error_view);
+ }
+
return $this->buildApplicationPage(
array(
$crumbs,
$this->renderConfigurationWarningIfRequired(),
$box,
),
array(
'title' => pht('Create Fragment'),
));
}
}
diff --git a/src/applications/phragment/controller/PhragmentSnapshotViewController.php b/src/applications/phragment/controller/PhragmentSnapshotViewController.php
index 006ba448c7..545e8806eb 100644
--- a/src/applications/phragment/controller/PhragmentSnapshotViewController.php
+++ b/src/applications/phragment/controller/PhragmentSnapshotViewController.php
@@ -1,155 +1,150 @@
<?php
final class PhragmentSnapshotViewController extends PhragmentController {
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id', '');
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$snapshot = id(new PhragmentSnapshotQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if ($snapshot === null) {
return new Aphront404Response();
}
$box = $this->createSnapshotView($snapshot);
$fragment = id(new PhragmentFragmentQuery())
->setViewer($viewer)
->withPHIDs(array($snapshot->getPrimaryFragmentPHID()))
->executeOne();
if ($fragment === null) {
return new Aphront404Response();
}
$parents = $this->loadParentFragments($fragment->getPath());
if ($parents === null) {
return new Aphront404Response();
}
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
$crumbs->addTextCrumb(pht('"%s" Snapshot', $snapshot->getName()));
$children = id(new PhragmentSnapshotChildQuery())
->setViewer($viewer)
->needFragments(true)
->needFragmentVersions(true)
->withSnapshotPHIDs(array($snapshot->getPHID()))
->execute();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($children as $child) {
$item = id(new PHUIObjectItemView())
->setHeader($child->getFragment()->getPath());
if ($child->getFragmentVersion() !== null) {
$item
->setHref($child->getFragmentVersion()->getURI())
->addAttribute(pht(
'Version %s',
$child->getFragmentVersion()->getSequence()));
} else {
$item
->setHref($child->getFragment()->getURI())
->addAttribute(pht('Directory'));
}
$list->addItem($item);
}
return $this->buildApplicationPage(
array(
$crumbs,
$this->renderConfigurationWarningIfRequired(),
$box,
$list,
),
array(
'title' => pht('View Snapshot'),
));
}
protected function createSnapshotView($snapshot) {
if ($snapshot === null) {
return null;
}
$viewer = $this->getRequest()->getUser();
- $phids = array();
- $phids[] = $snapshot->getPrimaryFragmentPHID();
-
- $this->loadHandles($phids);
-
$header = id(new PHUIHeaderView())
->setHeader(pht('"%s" Snapshot', $snapshot->getName()))
->setPolicyObject($snapshot)
->setUser($viewer);
$zip_uri = $this->getApplicationURI(
'zip@'.$snapshot->getName().
'/'.$snapshot->getPrimaryFragment()->getPath());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$snapshot,
PhabricatorPolicyCapability::CAN_EDIT);
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($snapshot)
->setObjectURI($snapshot->getURI());
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Snapshot as ZIP'))
->setHref($this->isCorrectlyConfigured() ? $zip_uri : null)
->setDisabled(!$this->isCorrectlyConfigured())
->setIcon('fa-floppy-o'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete Snapshot'))
->setHref($this->getApplicationURI(
'snapshot/delete/'.$snapshot->getID().'/'))
->setDisabled(!$can_edit)
->setWorkflow(true)
->setIcon('fa-times'));
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Promote Another Snapshot to Here'))
->setHref($this->getApplicationURI(
'snapshot/promote/'.$snapshot->getID().'/'))
->setDisabled(!$can_edit)
->setWorkflow(true)
->setIcon('fa-arrow-up'));
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($snapshot)
->setActionList($actions);
$properties->addProperty(
pht('Name'),
$snapshot->getName());
$properties->addProperty(
pht('Fragment'),
- $this->renderHandlesForPHIDs(array($snapshot->getPrimaryFragmentPHID())));
+ $viewer->renderHandle($snapshot->getPrimaryFragmentPHID()));
return id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
}
}
diff --git a/src/applications/phragment/controller/PhragmentVersionController.php b/src/applications/phragment/controller/PhragmentVersionController.php
index ceebd3fb96..9267fdc5b1 100644
--- a/src/applications/phragment/controller/PhragmentVersionController.php
+++ b/src/applications/phragment/controller/PhragmentVersionController.php
@@ -1,137 +1,132 @@
<?php
final class PhragmentVersionController extends PhragmentController {
private $id;
public function shouldAllowPublic() {
return true;
}
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id', 0);
}
public function processRequest() {
$request = $this->getRequest();
$viewer = $request->getUser();
$version = id(new PhragmentFragmentVersionQuery())
->setViewer($viewer)
->withIDs(array($this->id))
->executeOne();
if ($version === null) {
return new Aphront404Response();
}
$parents = $this->loadParentFragments($version->getFragment()->getPath());
if ($parents === null) {
return new Aphront404Response();
}
$current = idx($parents, count($parents) - 1, null);
$crumbs = $this->buildApplicationCrumbsWithPath($parents);
$crumbs->addTextCrumb(pht('View Version %d', $version->getSequence()));
- $phids = array();
- $phids[] = $version->getFilePHID();
-
- $this->loadHandles($phids);
-
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($version->getFilePHID()))
->executeOne();
if ($file !== null) {
$file_uri = $file->getDownloadURI();
}
$header = id(new PHUIHeaderView())
->setHeader(pht(
'%s at version %d',
$version->getFragment()->getName(),
$version->getSequence()))
->setPolicyObject($version)
->setUser($viewer);
$actions = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($version)
->setObjectURI($version->getURI());
$actions->addAction(
id(new PhabricatorActionView())
->setName(pht('Download Version'))
->setDisabled($file === null || !$this->isCorrectlyConfigured())
->setHref($this->isCorrectlyConfigured() ? $file_uri : null)
->setIcon('fa-download'));
$properties = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($version)
->setActionList($actions);
$properties->addProperty(
pht('File'),
- $this->renderHandlesForPHIDs(array($version->getFilePHID())));
+ $viewer->renderHandle($version->getFilePHID()));
$box = id(new PHUIObjectBoxView())
->setHeader($header)
->addPropertyList($properties);
return $this->buildApplicationPage(
array(
$crumbs,
$this->renderConfigurationWarningIfRequired(),
$box,
$this->renderPreviousVersionList($version),
),
array(
'title' => pht('View Version'),
));
}
private function renderPreviousVersionList(
PhragmentFragmentVersion $version) {
$request = $this->getRequest();
$viewer = $request->getUser();
$previous_versions = id(new PhragmentFragmentVersionQuery())
->setViewer($viewer)
->withFragmentPHIDs(array($version->getFragmentPHID()))
->withSequenceBefore($version->getSequence())
->execute();
$list = id(new PHUIObjectItemListView())
->setUser($viewer);
foreach ($previous_versions as $previous_version) {
$item = id(new PHUIObjectItemView());
$item->setHeader('Version '.$previous_version->getSequence());
$item->setHref($previous_version->getURI());
$item->addAttribute(phabricator_datetime(
$previous_version->getDateCreated(),
$viewer));
$patch_uri = $this->getApplicationURI(
'patch/'.$previous_version->getID().'/'.$version->getID());
$item->addAction(id(new PHUIListItemView())
->setIcon('fa-file-o')
->setName(pht('Get Patch'))
->setHref($this->isCorrectlyConfigured() ? $patch_uri : null)
->setDisabled(!$this->isCorrectlyConfigured()));
$list->addItem($item);
}
$item = id(new PHUIObjectItemView());
$item->setHeader('Prior to Version 0');
$item->addAttribute('Prior to any content (empty file)');
$item->addAction(id(new PHUIListItemView())
->setIcon('fa-file-o')
->setName(pht('Get Patch'))
->setHref($this->getApplicationURI(
'patch/x/'.$version->getID())));
$list->addItem($item);
return $list;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Wed, Jun 11, 12:20 AM (19 h, 12 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
141461
Default Alt Text
(116 KB)

Event Timeline