Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/files/controller/PhabricatorFileDataController.php b/src/applications/files/controller/PhabricatorFileDataController.php
index 0cc12a85ec..b292b6f4b8 100644
--- a/src/applications/files/controller/PhabricatorFileDataController.php
+++ b/src/applications/files/controller/PhabricatorFileDataController.php
@@ -1,278 +1,278 @@
<?php
final class PhabricatorFileDataController extends PhabricatorFileController {
private $phid;
private $key;
private $file;
public function shouldRequireLogin() {
return false;
}
public function shouldAllowPartialSessions() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$this->phid = $request->getURIData('phid');
$this->key = $request->getURIData('key');
$alt = PhabricatorEnv::getEnvConfig('security.alternate-file-domain');
$base_uri = PhabricatorEnv::getEnvConfig('phabricator.base-uri');
$alt_uri = new PhutilURI($alt);
$alt_domain = $alt_uri->getDomain();
$req_domain = $request->getHost();
$main_domain = id(new PhutilURI($base_uri))->getDomain();
$request_kind = $request->getURIData('kind');
$is_download = ($request_kind === 'download');
- if (!strlen($alt) || $main_domain == $alt_domain) {
+ if (!phutil_nonempty_string($alt) || $main_domain == $alt_domain) {
// No alternate domain.
$should_redirect = false;
$is_alternate_domain = false;
} else if ($req_domain != $alt_domain) {
// Alternate domain, but this request is on the main domain.
$should_redirect = true;
$is_alternate_domain = false;
} else {
// Alternate domain, and on the alternate domain.
$should_redirect = false;
$is_alternate_domain = true;
}
$response = $this->loadFile();
if ($response) {
return $response;
}
$file = $this->getFile();
if ($should_redirect) {
return id(new AphrontRedirectResponse())
->setIsExternal(true)
->setURI($file->getCDNURI($request_kind));
}
$response = new AphrontFileResponse();
$response->setCacheDurationInSeconds(60 * 60 * 24 * 30);
$response->setCanCDN($file->getCanCDN());
$begin = null;
$end = null;
// NOTE: It's important to accept "Range" requests when playing audio.
// If we don't, Safari has difficulty figuring out how long sounds are
// and glitches when trying to loop them. In particular, Safari sends
// an initial request for bytes 0-1 of the audio file, and things go south
// if we can't respond with a 206 Partial Content.
$range = $request->getHTTPHeader('range');
if (phutil_nonempty_string($range)) {
list($begin, $end) = $response->parseHTTPRange($range);
}
if (!$file->isViewableInBrowser()) {
$is_download = true;
}
$request_type = $request->getHTTPHeader('X-Phabricator-Request-Type');
$is_lfs = ($request_type == 'git-lfs');
if (!$is_download) {
$response->setMimeType($file->getViewableMimeType());
} else {
$is_post = $request->isHTTPPost();
$is_public = !$viewer->isLoggedIn();
// NOTE: Require POST to download files from the primary domain. If the
// request is not a POST request but arrives on the primary domain, we
// render a confirmation dialog. For discussion, see T13094.
// There are two exceptions to this rule:
// Git LFS requests can download with GET. This is safe (Git LFS won't
// execute files it downloads) and necessary to support Git LFS.
// Requests with no credentials may also download with GET. This
// primarily supports downloading files with `arc download` or other
// API clients. This is only "mostly" safe: if you aren't logged in, you
// are likely immune to XSS and CSRF. However, an attacker may still be
// able to set cookies on this domain (for example, to fixate your
// session). For now, we accept these risks because users running
// Phabricator in this mode are knowingly accepting a security risk
// against setup advice, and there's significant value in having
// API development against test and production installs work the same
// way.
$is_safe = ($is_alternate_domain || $is_post || $is_lfs || $is_public);
if (!$is_safe) {
return $this->newDialog()
->setSubmitURI($file->getDownloadURI())
->setTitle(pht('Download File'))
->appendParagraph(
pht(
'Download file %s (%s)?',
phutil_tag('strong', array(), $file->getName()),
phutil_format_bytes($file->getByteSize())))
->addCancelButton($file->getURI())
->addSubmitButton(pht('Download File'));
}
$response->setMimeType($file->getMimeType());
$response->setDownload($file->getName());
}
$iterator = $file->getFileDataIterator($begin, $end);
$response->setContentLength($file->getByteSize());
$response->setContentIterator($iterator);
// In Chrome, we must permit this domain in "object-src" CSP when serving a
// PDF or the browser will refuse to render it.
if (!$is_download && $file->isPDF()) {
$request_uri = id(clone $request->getAbsoluteRequestURI())
->setPath(null)
->setFragment(null)
->removeAllQueryParams();
$response->addContentSecurityPolicyURI(
'object-src',
(string)$request_uri);
}
if ($this->shouldCompressFileDataResponse($file)) {
$response->setCompressResponse(true);
}
return $response;
}
private function loadFile() {
// Access to files is provided by knowledge of a per-file secret key in
// the URI. Knowledge of this secret is sufficient to retrieve the file.
// For some requests, we also have a valid viewer. However, for many
// requests (like alternate domain requests or Git LFS requests) we will
// not. Even if we do have a valid viewer, use the omnipotent viewer to
// make this logic simpler and more consistent.
// Beyond making the policy check itself more consistent, this also makes
// sure we're consistent about returning HTTP 404 on bad requests instead
// of serving HTTP 200 with a login page, which can mislead some clients.
$viewer = PhabricatorUser::getOmnipotentUser();
$file = id(new PhabricatorFileQuery())
->setViewer($viewer)
->withPHIDs(array($this->phid))
->withIsDeleted(false)
->executeOne();
if (!$file) {
return new Aphront404Response();
}
// We may be on the CDN domain, so we need to use a fully-qualified URI
// here to make sure we end up back on the main domain.
$info_uri = PhabricatorEnv::getURI($file->getInfoURI());
if (!$file->validateSecretKey($this->key)) {
$dialog = $this->newDialog()
->setTitle(pht('Invalid Authorization'))
->appendParagraph(
pht(
'The link you followed to access this file is no longer '.
'valid. The visibility of the file may have changed after '.
'the link was generated.'))
->appendParagraph(
pht(
'You can continue to the file detail page to get more '.
'information and attempt to access the file.'))
->addCancelButton($info_uri, pht('Continue'));
return id(new AphrontDialogResponse())
->setDialog($dialog)
->setHTTPResponseCode(404);
}
if ($file->getIsPartial()) {
$dialog = $this->newDialog()
->setTitle(pht('Partial Upload'))
->appendParagraph(
pht(
'This file has only been partially uploaded. It must be '.
'uploaded completely before you can download it.'))
->appendParagraph(
pht(
'You can continue to the file detail page to monitor the '.
'upload progress of the file.'))
->addCancelButton($info_uri, pht('Continue'));
return id(new AphrontDialogResponse())
->setDialog($dialog)
->setHTTPResponseCode(404);
}
$this->file = $file;
return null;
}
private function getFile() {
if (!$this->file) {
throw new PhutilInvalidStateException('loadFile');
}
return $this->file;
}
private function shouldCompressFileDataResponse(PhabricatorFile $file) {
// If the client sends "Accept-Encoding: gzip", we have the option of
// compressing the response.
// We generally expect this to be a good idea if the file compresses well,
// but maybe not such a great idea if the file is already compressed (like
// an image or video) or compresses poorly: the CPU cost of compressing and
// decompressing the stream may exceed the bandwidth savings during
// transfer.
// Ideally, we'd probably make this decision by compressing files when
// they are uploaded, storing the compressed size, and then doing a test
// here using the compression savings and estimated transfer speed.
// For now, just guess that we shouldn't compress images or videos or
// files that look like they are already compressed, and should compress
// everything else.
if ($file->isViewableImage()) {
return false;
}
if ($file->isAudio()) {
return false;
}
if ($file->isVideo()) {
return false;
}
$compressed_types = array(
'application/x-gzip',
'application/x-compress',
'application/x-compressed',
'application/x-zip-compressed',
'application/zip',
);
$compressed_types = array_fuse($compressed_types);
$mime_type = $file->getMimeType();
if (isset($compressed_types[$mime_type])) {
return false;
}
return true;
}
}
diff --git a/src/applications/search/field/PhabricatorSearchDateField.php b/src/applications/search/field/PhabricatorSearchDateField.php
index 41decd9503..8f43494222 100644
--- a/src/applications/search/field/PhabricatorSearchDateField.php
+++ b/src/applications/search/field/PhabricatorSearchDateField.php
@@ -1,54 +1,54 @@
<?php
final class PhabricatorSearchDateField
extends PhabricatorSearchField {
protected function newControl() {
return id(new AphrontFormTextControl())
->setPlaceholder(pht('"2022-12-25" or "7 days ago"...'));
}
protected function getValueFromRequest(AphrontRequest $request, $key) {
return $request->getStr($key);
}
public function getValueForQuery($value) {
return $this->parseDateTime($value);
}
protected function validateControlValue($value) {
- if (!strlen($value)) {
+ if (!phutil_nonempty_scalar($value)) {
return;
}
$epoch = $this->parseDateTime($value);
if ($epoch) {
return;
}
$this->addError(
pht('Invalid'),
pht('Date value for "%s" can not be parsed.', $this->getLabel()));
}
protected function parseDateTime($value) {
- if (!strlen($value)) {
+ if (!phutil_nonempty_scalar($value)) {
return null;
}
// If this appears to be an epoch timestamp, just return it unmodified.
// This assumes values like "2016" or "20160101" are "Ymd".
if (is_int($value) || ctype_digit($value)) {
if ((int)$value > 30000000) {
return (int)$value;
}
}
return PhabricatorTime::parseLocalTime($value, $this->getViewer());
}
protected function newConduitParameterType() {
return new ConduitEpochParameterType();
}
}
diff --git a/src/view/layout/AphrontSideNavFilterView.php b/src/view/layout/AphrontSideNavFilterView.php
index 6d255f2b30..c7c8b5b534 100644
--- a/src/view/layout/AphrontSideNavFilterView.php
+++ b/src/view/layout/AphrontSideNavFilterView.php
@@ -1,272 +1,281 @@
<?php
/**
* Provides a navigation sidebar. For example:
*
* $nav = new AphrontSideNavFilterView();
* $nav
* ->setBaseURI($some_uri)
* ->addLabel('Cats')
* ->addFilter('meow', 'Meow')
* ->addFilter('purr', 'Purr')
* ->addLabel('Dogs')
* ->addFilter('woof', 'Woof')
* ->addFilter('bark', 'Bark');
* $valid_filter = $nav->selectFilter($user_selection, $default = 'meow');
*
*/
final class AphrontSideNavFilterView extends AphrontView {
private $items = array();
private $baseURI;
private $selectedFilter = false;
private $menu;
private $crumbs;
private $classes = array();
private $menuID;
private $mainID;
private $isProfileMenu;
private $footer = array();
public function setMenuID($menu_id) {
$this->menuID = $menu_id;
return $this;
}
public function getMenuID() {
return $this->menuID;
}
public function __construct() {
$this->menu = new PHUIListView();
}
public function addClass($class) {
$this->classes[] = $class;
return $this;
}
public function setCrumbs(PHUICrumbsView $crumbs) {
$this->crumbs = $crumbs;
return $this;
}
public function getCrumbs() {
return $this->crumbs;
}
public function setIsProfileMenu($is_profile) {
$this->isProfileMenu = $is_profile;
return $this;
}
public function getIsProfileMenu() {
return $this->isProfileMenu;
}
public function getMenuView() {
return $this->menu;
}
public function addMenuItem(PHUIListItemView $item) {
$this->menu->addMenuItem($item);
return $this;
}
public function getMenu() {
return $this->menu;
}
public function addFilter($key, $name, $uri = null, $icon = null) {
return $this->addThing(
$key, $name, $uri, PHUIListItemView::TYPE_LINK, $icon);
}
public function addButton($key, $name, $uri = null) {
return $this->addThing(
$key, $name, $uri, PHUIListItemView::TYPE_BUTTON);
}
public function newLink($key) {
$this->addFilter($key, '');
return $this->getMenuView()->getItem($key);
}
+ /**
+ * Add a thing in the menu
+ *
+ * @param string $key Internal name
+ * @param string $name Human name
+ * @param mixed $uri Destination URI. For example as string or as PhutilURI.
+ * @param string $type Item type. For example see PHUIListItemView constants.
+ * @param string $icon Icon name
+ */
private function addThing($key, $name, $uri, $type, $icon = null) {
$item = id(new PHUIListItemView())
->setName($name)
->setType($type);
- if (strlen($icon)) {
+ if (phutil_nonempty_string($icon)) {
$item->setIcon($icon);
}
if (strlen($key)) {
$item->setKey($key);
}
if ($uri) {
$item->setHref($uri);
} else {
$href = clone $this->baseURI;
$href->setPath(rtrim($href->getPath().$key, '/').'/');
$href = (string)$href;
$item->setHref($href);
}
return $this->addMenuItem($item);
}
public function addCustomBlock($block) {
$this->menu->addMenuItem(
id(new PHUIListItemView())
->setType(PHUIListItemView::TYPE_CUSTOM)
->appendChild($block));
return $this;
}
public function addLabel($name) {
return $this->addMenuItem(
id(new PHUIListItemView())
->setType(PHUIListItemView::TYPE_LABEL)
->setName($name));
}
public function setBaseURI(PhutilURI $uri) {
$this->baseURI = $uri;
return $this;
}
public function getBaseURI() {
return $this->baseURI;
}
public function selectFilter($key, $default = null) {
$this->selectedFilter = $default;
if ($this->menu->getItem($key) && phutil_nonempty_string($key)) {
$this->selectedFilter = $key;
}
return $this->selectedFilter;
}
public function getSelectedFilter() {
return $this->selectedFilter;
}
public function appendFooter($footer) {
$this->footer[] = $footer;
return $this;
}
public function getMainID() {
if (!$this->mainID) {
$this->mainID = celerity_generate_unique_node_id();
}
return $this->mainID;
}
public function render() {
if ($this->menu->getItems()) {
if (!$this->baseURI) {
throw new PhutilInvalidStateException('setBaseURI');
}
if ($this->selectedFilter === false) {
throw new PhutilInvalidStateException('selectFilter');
}
}
if ($this->selectedFilter !== null) {
$selected_item = $this->menu->getItem($this->selectedFilter);
if ($selected_item) {
$selected_item->addClass('phui-list-item-selected');
}
}
require_celerity_resource('phui-basic-nav-view-css');
return $this->renderFlexNav();
}
private function renderFlexNav() {
require_celerity_resource('phabricator-nav-view-css');
$nav_classes = array();
$nav_classes[] = 'phabricator-nav';
$nav_id = null;
$drag_id = null;
$content_id = celerity_generate_unique_node_id();
$local_id = null;
$background_id = null;
$local_menu = null;
$main_id = $this->getMainID();
$nav_menu = null;
if ($this->menu->getItems()) {
$local_id = celerity_generate_unique_node_id();
$background_id = celerity_generate_unique_node_id();
$nav_classes[] = 'has-local-nav';
$local_menu = phutil_tag(
'div',
array(
'class' => 'phabricator-nav-local phabricator-side-menu',
'id' => $local_id,
),
$this->menu->setID($this->getMenuID()));
}
$crumbs = null;
if ($this->crumbs) {
$crumbs = $this->crumbs->render();
$nav_classes[] = 'has-crumbs';
}
$nav_classes = array_merge($nav_classes, $this->classes);
$menu = phutil_tag(
'div',
array(
'class' => implode(' ', $nav_classes),
'id' => $main_id,
),
array(
$local_menu,
phutil_tag(
'div',
array(
'class' => 'phabricator-nav-content plb',
'id' => $content_id,
),
array(
$crumbs,
$this->renderChildren(),
$this->footer,
)),
));
$classes = array();
$classes[] = 'phui-navigation-shell';
if ($this->getIsProfileMenu()) {
$classes[] = 'phui-profile-menu phui-basic-nav';
} else {
$classes[] = 'phui-basic-nav';
}
$shell = phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
array(
$menu,
));
return $shell;
}
}

File Metadata

Mime Type
text/x-diff
Expires
Fri, Mar 14, 6:52 AM (3 h, 5 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
71646
Default Alt Text
(18 KB)

Event Timeline