Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/calendar/util/CalendarTimeUtil.php b/src/applications/calendar/util/CalendarTimeUtil.php
index 0fc4f2e527..6051ade059 100644
--- a/src/applications/calendar/util/CalendarTimeUtil.php
+++ b/src/applications/calendar/util/CalendarTimeUtil.php
@@ -1,89 +1,90 @@
<?php
+
/**
* This class is useful for generating various time objects, relative to the
* user and their timezone.
*
* For now, the class exposes two sets of static methods for the two main
* calendar views - one for the conpherence calendar widget and one for the
* user profile calendar view. These have slight differences such as
* conpherence showing both a three day "today 'til 2 days from now" *and*
* a Sunday -> Saturday list, whilest the profile view shows a more simple
* seven day rolling list of events.
*/
final class CalendarTimeUtil extends Phobject {
public static function getCalendarEventEpochs(
PhabricatorUser $user,
$start_day_str = 'Sunday',
$days = 9) {
$objects = self::getStartDateTimeObjects($user, $start_day_str);
$start_day = $objects['start_day'];
$end_day = clone $start_day;
$end_day->modify('+'.$days.' days');
return array(
'start_epoch' => $start_day->format('U'),
'end_epoch' => $end_day->format('U'),
);
}
public static function getCalendarWeekTimestamps(
PhabricatorUser $user) {
return self::getTimestamps($user, 'Today', 7);
}
public static function getCalendarWidgetTimestamps(
PhabricatorUser $user) {
return self::getTimestamps($user, 'Sunday', 9);
}
/**
* Public for testing purposes only. You should probably use one of the
* functions above.
*/
public static function getTimestamps(
PhabricatorUser $user,
$start_day_str,
$days) {
$objects = self::getStartDateTimeObjects($user, $start_day_str);
$start_day = $objects['start_day'];
$timestamps = array();
for ($day = 0; $day < $days; $day++) {
$timestamp = clone $start_day;
$timestamp->modify(sprintf('+%d days', $day));
$timestamps[] = $timestamp;
}
return array(
'today' => $objects['today'],
'epoch_stamps' => $timestamps,
);
}
private static function getStartDateTimeObjects(
PhabricatorUser $user,
$start_day_str) {
$timezone = new DateTimeZone($user->getTimezoneIdentifier());
$today_epoch = PhabricatorTime::parseLocalTime('today', $user);
$today = new DateTime('@'.$today_epoch);
$today->setTimeZone($timezone);
if (strtolower($start_day_str) == 'today' ||
$today->format('l') == $start_day_str) {
$start_day = clone $today;
} else {
$start_epoch = PhabricatorTime::parseLocalTime(
'last '.$start_day_str,
$user);
$start_day = new DateTime('@'.$start_epoch);
$start_day->setTimeZone($timezone);
}
return array(
'today' => $today,
'start_day' => $start_day,
);
}
}
diff --git a/src/applications/diviner/cache/DivinerAtomCache.php b/src/applications/diviner/cache/DivinerAtomCache.php
index b37bd71419..8ef237324b 100644
--- a/src/applications/diviner/cache/DivinerAtomCache.php
+++ b/src/applications/diviner/cache/DivinerAtomCache.php
@@ -1,233 +1,233 @@
<?php
final class DivinerAtomCache extends DivinerDiskCache {
private $fileHashMap;
private $atomMap;
private $symbolMap;
private $edgeSrcMap;
private $edgeDstMap;
private $graphMap;
private $atoms = array();
private $writeAtoms = array();
public function __construct($cache_directory) {
- return parent::__construct($cache_directory, 'diviner-atom-cache');
+ parent::__construct($cache_directory, 'diviner-atom-cache');
}
public function delete() {
parent::delete();
$this->fileHashMap = null;
$this->atomMap = null;
$this->atoms = array();
return $this;
}
/* -( File Hash Map )------------------------------------------------------ */
public function getFileHashMap() {
if ($this->fileHashMap === null) {
$this->fileHashMap = $this->getCache()->getKey('file', array());
}
return $this->fileHashMap;
}
public function addFileHash($file_hash, $atom_hash) {
$this->getFileHashMap();
$this->fileHashMap[$file_hash] = $atom_hash;
return $this;
}
public function fileHashExists($file_hash) {
$map = $this->getFileHashMap();
return isset($map[$file_hash]);
}
public function deleteFileHash($file_hash) {
if ($this->fileHashExists($file_hash)) {
$map = $this->getFileHashMap();
$atom_hash = $map[$file_hash];
unset($this->fileHashMap[$file_hash]);
$this->deleteAtomHash($atom_hash);
}
return $this;
}
/* -( Atom Map )----------------------------------------------------------- */
public function getAtomMap() {
if ($this->atomMap === null) {
$this->atomMap = $this->getCache()->getKey('atom', array());
}
return $this->atomMap;
}
public function getAtom($atom_hash) {
if (!array_key_exists($atom_hash, $this->atoms)) {
$key = 'atom/'.$this->getHashKey($atom_hash);
$this->atoms[$atom_hash] = $this->getCache()->getKey($key);
}
return $this->atoms[$atom_hash];
}
public function addAtom(array $atom) {
$hash = $atom['hash'];
$this->atoms[$hash] = $atom;
$this->getAtomMap();
$this->atomMap[$hash] = true;
$this->writeAtoms['atom/'.$this->getHashKey($hash)] = $atom;
return $this;
}
public function deleteAtomHash($atom_hash) {
$atom = $this->getAtom($atom_hash);
if ($atom) {
foreach ($atom['childHashes'] as $child_hash) {
$this->deleteAtomHash($child_hash);
}
}
$this->getAtomMap();
unset($this->atomMap[$atom_hash]);
unset($this->writeAtoms[$atom_hash]);
$this->getCache()->deleteKey('atom/'.$this->getHashKey($atom_hash));
return $this;
}
public function saveAtoms() {
$this->getCache()->setKeys(
array(
'file' => $this->getFileHashMap(),
'atom' => $this->getAtomMap(),
) + $this->writeAtoms);
$this->writeAtoms = array();
return $this;
}
/* -( Symbol Hash Map )---------------------------------------------------- */
public function getSymbolMap() {
if ($this->symbolMap === null) {
$this->symbolMap = $this->getCache()->getKey('symbol', array());
}
return $this->symbolMap;
}
public function addSymbol($atom_hash, $symbol_hash) {
$this->getSymbolMap();
$this->symbolMap[$atom_hash] = $symbol_hash;
return $this;
}
public function deleteSymbol($atom_hash) {
$this->getSymbolMap();
unset($this->symbolMap[$atom_hash]);
return $this;
}
public function saveSymbols() {
$this->getCache()->setKeys(
array(
'symbol' => $this->getSymbolMap(),
));
return $this;
}
/* -( Edge Map )----------------------------------------------------------- */
public function getEdgeMap() {
if ($this->edgeDstMap === null) {
$this->edgeDstMap = $this->getCache()->getKey('edge', array());
$this->edgeSrcMap = array();
foreach ($this->edgeDstMap as $dst => $srcs) {
foreach ($srcs as $src => $ignored) {
$this->edgeSrcMap[$src][$dst] = true;
}
}
}
return $this->edgeDstMap;
}
public function getEdgesWithDestination($symbol_hash) {
$this->getEdgeMap();
return array_keys(idx($this->edgeDstMap, $symbol_hash, array()));
}
public function addEdges($node_hash, array $symbol_hash_list) {
$this->getEdgeMap();
$this->edgeSrcMap[$node_hash] = array_fill_keys($symbol_hash_list, true);
foreach ($symbol_hash_list as $symbol_hash) {
$this->edgeDstMap[$symbol_hash][$node_hash] = true;
}
return $this;
}
public function deleteEdges($node_hash) {
$this->getEdgeMap();
foreach (idx($this->edgeSrcMap, $node_hash, array()) as $dst => $ignored) {
unset($this->edgeDstMap[$dst][$node_hash]);
if (empty($this->edgeDstMap[$dst])) {
unset($this->edgeDstMap[$dst]);
}
}
unset($this->edgeSrcMap[$node_hash]);
return $this;
}
public function saveEdges() {
$this->getCache()->setKeys(
array(
'edge' => $this->getEdgeMap(),
));
return $this;
}
/* -( Graph Map )---------------------------------------------------------- */
public function getGraphMap() {
if ($this->graphMap === null) {
$this->graphMap = $this->getCache()->getKey('graph', array());
}
return $this->graphMap;
}
public function deleteGraph($node_hash) {
$this->getGraphMap();
unset($this->graphMap[$node_hash]);
return $this;
}
public function addGraph($node_hash, $graph_hash) {
$this->getGraphMap();
$this->graphMap[$node_hash] = $graph_hash;
return $this;
}
public function saveGraph() {
$this->getCache()->setKeys(
array(
'graph' => $this->getGraphMap(),
));
return $this;
}
}
diff --git a/src/applications/diviner/cache/DivinerPublishCache.php b/src/applications/diviner/cache/DivinerPublishCache.php
index e1bff354dd..1b3859b3e9 100644
--- a/src/applications/diviner/cache/DivinerPublishCache.php
+++ b/src/applications/diviner/cache/DivinerPublishCache.php
@@ -1,74 +1,74 @@
<?php
final class DivinerPublishCache extends DivinerDiskCache {
private $pathMap;
private $index;
public function __construct($cache_directory) {
- return parent::__construct($cache_directory, 'diviner-publish-cache');
+ parent::__construct($cache_directory, 'diviner-publish-cache');
}
/* -( Path Map )----------------------------------------------------------- */
public function getPathMap() {
if ($this->pathMap === null) {
$this->pathMap = $this->getCache()->getKey('path', array());
}
return $this->pathMap;
}
public function writePathMap() {
$this->getCache()->setKey('path', $this->getPathMap());
}
public function getAtomPathsFromCache($hash) {
return idx($this->getPathMap(), $hash, array());
}
public function removeAtomPathsFromCache($hash) {
$map = $this->getPathMap();
unset($map[$hash]);
$this->pathMap = $map;
return $this;
}
public function addAtomPathsToCache($hash, array $paths) {
$map = $this->getPathMap();
$map[$hash] = $paths;
$this->pathMap = $map;
return $this;
}
/* -( Index )-------------------------------------------------------------- */
public function getIndex() {
if ($this->index === null) {
$this->index = $this->getCache()->getKey('index', array());
}
return $this->index;
}
public function writeIndex() {
$this->getCache()->setKey('index', $this->getIndex());
}
public function deleteAtomFromIndex($hash) {
$index = $this->getIndex();
unset($index[$hash]);
$this->index = $index;
return $this;
}
public function addAtomToIndex($hash, array $data) {
$index = $this->getIndex();
$index[$hash] = $data;
$this->index = $index;
return $this;
}
}
diff --git a/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php b/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php
index 61301bdae5..eb6288b40f 100644
--- a/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php
+++ b/src/applications/files/engine/PhabricatorChunkedFileStorageEngine.php
@@ -1,188 +1,188 @@
<?php
final class PhabricatorChunkedFileStorageEngine
extends PhabricatorFileStorageEngine {
public function getEngineIdentifier() {
return 'chunks';
}
public function getEnginePriority() {
return 60000;
}
/**
* We can write chunks if we have at least one valid storage engine
* underneath us.
*/
public function canWriteFiles() {
return (bool)$this->getWritableEngine();
}
public function hasFilesizeLimit() {
return false;
}
public function isChunkEngine() {
return true;
}
public function writeFile($data, array $params) {
// The chunk engine does not support direct writes.
throw new PhutilMethodNotImplementedException();
}
public function readFile($handle) {
// This is inefficient, but makes the API work as expected.
$chunks = $this->loadAllChunks($handle, true);
$buffer = '';
foreach ($chunks as $chunk) {
$data_file = $chunk->getDataFile();
if (!$data_file) {
throw new Exception(pht('This file data is incomplete!'));
}
$buffer .= $chunk->getDataFile()->loadFileData();
}
return $buffer;
}
public function deleteFile($handle) {
$engine = new PhabricatorDestructionEngine();
$chunks = $this->loadAllChunks($handle, true);
foreach ($chunks as $chunk) {
$engine->destroyObject($chunk);
}
}
private function loadAllChunks($handle, $need_files) {
$chunks = id(new PhabricatorFileChunkQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withChunkHandles(array($handle))
->needDataFiles($need_files)
->execute();
$chunks = msort($chunks, 'getByteStart');
return $chunks;
}
/**
* Compute a chunked file hash for the viewer.
*
* We can not currently compute a real hash for chunked file uploads (because
* no process sees all of the file data).
*
* We also can not trust the hash that the user claims to have computed. If
* we trust the user, they can upload some `evil.exe` and claim it has the
* same file hash as `good.exe`. When another user later uploads the real
* `good.exe`, we'll just create a reference to the existing `evil.exe`. Users
* who download `good.exe` will then receive `evil.exe`.
*
* Instead, we rehash the user's claimed hash with account secrets. This
* allows users to resume file uploads, but not collide with other users.
*
* Ideally, we'd like to be able to verify hashes, but this is complicated
* and time consuming and gives us a fairly small benefit.
*
* @param PhabricatorUser Viewing user.
* @param string Claimed file hash.
* @return string Rehashed file hash.
*/
public static function getChunkedHash(PhabricatorUser $viewer, $hash) {
if (!$viewer->getPHID()) {
throw new Exception(
pht('Unable to compute chunked hash without real viewer!'));
}
$input = $viewer->getAccountSecret().':'.$hash.':'.$viewer->getPHID();
return self::getChunkedHashForInput($input);
}
public static function getChunkedHashForInput($input) {
$rehash = PhabricatorHash::digest($input);
// Add a suffix to identify this as a chunk hash.
$rehash = substr($rehash, 0, -2).'-C';
return $rehash;
}
public function allocateChunks($length, array $properties) {
$file = PhabricatorFile::newChunkedFile($this, $length, $properties);
$chunk_size = $this->getChunkSize();
$handle = $file->getStorageHandle();
$chunks = array();
for ($ii = 0; $ii < $length; $ii += $chunk_size) {
$chunks[] = PhabricatorFileChunk::initializeNewChunk(
$handle,
$ii,
min($ii + $chunk_size, $length));
}
$file->openTransaction();
foreach ($chunks as $chunk) {
$chunk->save();
}
$file->save();
$file->saveTransaction();
return $file;
}
/**
* Find a storage engine which is suitable for storing chunks.
*
* This engine must be a writable engine, have a filesize limit larger than
* the chunk limit, and must not be a chunk engine itself.
*/
private function getWritableEngine() {
// NOTE: We can't just load writable engines or we'll loop forever.
- $engines = PhabricatorFileStorageEngine::loadAllEngines();
+ $engines = parent::loadAllEngines();
foreach ($engines as $engine) {
if ($engine->isChunkEngine()) {
continue;
}
if ($engine->isTestEngine()) {
continue;
}
if (!$engine->canWriteFiles()) {
continue;
}
if ($engine->hasFilesizeLimit()) {
if ($engine->getFilesizeLimit() < $this->getChunkSize()) {
continue;
}
}
return true;
}
return false;
}
public function getChunkSize() {
return (4 * 1024 * 1024);
}
public function getFileDataIterator(PhabricatorFile $file, $begin, $end) {
$chunks = id(new PhabricatorFileChunkQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withChunkHandles(array($file->getStorageHandle()))
->withByteRange($begin, $end)
->needDataFiles(true)
->execute();
return new PhabricatorFileChunkIterator($chunks, $begin, $end);
}
}
diff --git a/src/applications/home/controller/PhabricatorHomeMainController.php b/src/applications/home/controller/PhabricatorHomeMainController.php
index 330aecfca1..63ce763b69 100644
--- a/src/applications/home/controller/PhabricatorHomeMainController.php
+++ b/src/applications/home/controller/PhabricatorHomeMainController.php
@@ -1,422 +1,422 @@
<?php
final class PhabricatorHomeMainController extends PhabricatorHomeController {
private $minipanels = array();
public function shouldAllowPublic() {
return true;
}
public function isGlobalDragAndDropUploadEnabled() {
return true;
}
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$dashboard = PhabricatorDashboardInstall::getDashboard(
$user,
$user->getPHID(),
get_class($this->getCurrentApplication()));
if (!$dashboard) {
$dashboard = PhabricatorDashboardInstall::getDashboard(
$user,
PhabricatorHomeApplication::DASHBOARD_DEFAULT,
get_class($this->getCurrentApplication()));
}
if ($dashboard) {
$content = id(new PhabricatorDashboardRenderingEngine())
->setViewer($user)
->setDashboard($dashboard)
->renderDashboard();
} else {
$project_query = new PhabricatorProjectQuery();
$project_query->setViewer($user);
$project_query->withMemberPHIDs(array($user->getPHID()));
$projects = $project_query->execute();
$content = $this->buildMainResponse($projects);
}
if (!$request->getURIData('only')) {
$nav = $this->buildNav();
$nav->appendChild(
array(
$content,
id(new PhabricatorGlobalUploadTargetView())->setUser($user),
));
$content = $nav;
}
return $this->buildApplicationPage(
$content,
array(
'title' => 'Phabricator',
));
}
private function buildMainResponse(array $projects) {
assert_instances_of($projects, 'PhabricatorProject');
$viewer = $this->getRequest()->getUser();
$has_maniphest = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorManiphestApplication',
$viewer);
$has_audit = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorAuditApplication',
$viewer);
$has_differential = PhabricatorApplication::isClassInstalledForViewer(
'PhabricatorDifferentialApplication',
$viewer);
if ($has_maniphest) {
$unbreak_panel = $this->buildUnbreakNowPanel();
$triage_panel = $this->buildNeedsTriagePanel($projects);
$tasks_panel = $this->buildTasksPanel();
} else {
$unbreak_panel = null;
$triage_panel = null;
$tasks_panel = null;
}
if ($has_audit) {
$audit_panel = $this->buildAuditPanel();
$commit_panel = $this->buildCommitPanel();
} else {
$audit_panel = null;
$commit_panel = null;
}
if (PhabricatorEnv::getEnvConfig('welcome.html') !== null) {
$welcome_panel = $this->buildWelcomePanel();
} else {
$welcome_panel = null;
}
if ($has_differential) {
$revision_panel = $this->buildRevisionPanel();
} else {
$revision_panel = null;
}
$home = phutil_tag(
'div',
array(
'class' => 'homepage-panel',
),
array(
$welcome_panel,
$unbreak_panel,
$triage_panel,
$revision_panel,
$tasks_panel,
$audit_panel,
$commit_panel,
$this->minipanels,
));
return $home;
}
private function buildUnbreakNowPanel() {
$unbreak_now = PhabricatorEnv::getEnvConfig(
'maniphest.priorities.unbreak-now');
if (!$unbreak_now) {
return null;
}
$user = $this->getRequest()->getUser();
$task_query = id(new ManiphestTaskQuery())
->setViewer($user)
->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
->withPriorities(array($unbreak_now))
->needProjectPHIDs(true)
->setLimit(10);
$tasks = $task_query->execute();
if (!$tasks) {
return $this->renderMiniPanel(
pht('No "Unbreak Now!" Tasks'),
pht('Nothing appears to be critically broken right now.'));
}
$href = urisprintf(
'/maniphest/?statuses=open()&priorities=%s#R',
$unbreak_now);
$title = pht('Unbreak Now!');
$panel = new PHUIObjectBoxView();
$panel->setHeader($this->renderSectionHeader($title, $href));
$panel->setObjectList($this->buildTaskListView($tasks));
return $panel;
}
private function buildNeedsTriagePanel(array $projects) {
assert_instances_of($projects, 'PhabricatorProject');
$needs_triage = PhabricatorEnv::getEnvConfig(
'maniphest.priorities.needs-triage');
if (!$needs_triage) {
return null;
}
$user = $this->getRequest()->getUser();
if (!$user->isLoggedIn()) {
return null;
}
if ($projects) {
$task_query = id(new ManiphestTaskQuery())
->setViewer($user)
->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
->withPriorities(array($needs_triage))
->withEdgeLogicPHIDs(
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
PhabricatorQueryConstraint::OPERATOR_OR,
mpull($projects, 'getPHID'))
->needProjectPHIDs(true)
->setLimit(10);
$tasks = $task_query->execute();
} else {
$tasks = array();
}
if (!$tasks) {
return $this->renderMiniPanel(
pht('No "Needs Triage" Tasks'),
pht('No tasks in projects you are a member of need triage.'));
}
$title = pht('Needs Triage');
$href = urisprintf(
'/maniphest/?statuses=open()&priorities=%s&projects=projects(%s)#R',
$needs_triage,
$user->getPHID());
$panel = new PHUIObjectBoxView();
$panel->setHeader($this->renderSectionHeader($title, $href));
$panel->setObjectList($this->buildTaskListView($tasks));
return $panel;
}
private function buildRevisionPanel() {
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();
$revision_query = id(new DifferentialRevisionQuery())
->setViewer($user)
->withStatus(DifferentialRevisionQuery::STATUS_OPEN)
->withResponsibleUsers(array($user_phid))
->needRelationships(true)
->needFlags(true)
->needDrafts(true);
$revisions = $revision_query->execute();
- list($blocking, $active,) = DifferentialRevisionQuery::splitResponsible(
+ list($blocking, $active) = DifferentialRevisionQuery::splitResponsible(
$revisions,
array($user_phid));
if (!$blocking && !$active) {
return $this->renderMiniPanel(
pht('No Waiting Revisions'),
pht('No revisions are waiting on you.'));
}
$title = pht('Revisions Waiting on You');
$href = '/differential';
$panel = new PHUIObjectBoxView();
$panel->setHeader($this->renderSectionHeader($title, $href));
$revision_view = id(new DifferentialRevisionListView())
->setHighlightAge(true)
->setRevisions(array_merge($blocking, $active))
->setUser($user);
$phids = array_merge(
array($user_phid),
$revision_view->getRequiredHandlePHIDs());
$handles = $this->loadViewerHandles($phids);
$revision_view->setHandles($handles);
$list_view = $revision_view->render();
$panel->setObjectList($list_view);
return $panel;
}
private function buildWelcomePanel() {
$panel = new PHUIObjectBoxView();
$panel->setHeaderText(pht('Welcome'));
$panel->appendChild(
phutil_safe_html(
PhabricatorEnv::getEnvConfig('welcome.html')));
return $panel;
}
private function buildTasksPanel() {
$user = $this->getRequest()->getUser();
$user_phid = $user->getPHID();
$task_query = id(new ManiphestTaskQuery())
->setViewer($user)
->withStatuses(ManiphestTaskStatus::getOpenStatusConstants())
->setGroupBy(ManiphestTaskQuery::GROUP_PRIORITY)
->withOwners(array($user_phid))
->needProjectPHIDs(true)
->setLimit(10);
$tasks = $task_query->execute();
if (!$tasks) {
return $this->renderMiniPanel(
pht('No Assigned Tasks'),
pht('You have no assigned tasks.'));
}
$title = pht('Assigned Tasks');
$href = '/maniphest/query/assigned/';
$panel = new PHUIObjectBoxView();
$panel->setHeader($this->renderSectionHeader($title, $href));
$panel->setObjectList($this->buildTaskListView($tasks));
return $panel;
}
private function buildTaskListView(array $tasks) {
assert_instances_of($tasks, 'ManiphestTask');
$user = $this->getRequest()->getUser();
$phids = array_merge(
array_filter(mpull($tasks, 'getOwnerPHID')),
array_mergev(mpull($tasks, 'getProjectPHIDs')));
$handles = $this->loadViewerHandles($phids);
$view = new ManiphestTaskListView();
$view->setTasks($tasks);
$view->setUser($user);
$view->setHandles($handles);
return $view;
}
private function renderSectionHeader($title, $href) {
$title = phutil_tag(
'a',
array(
'href' => $href,
),
$title);
$icon = id(new PHUIIconView())
->setIconFont('fa-search')
->setHref($href);
$header = id(new PHUIHeaderView())
->setHeader($title)
->addActionIcon($icon);
return $header;
}
private function renderMiniPanel($title, $body) {
$panel = new PHUIInfoView();
$panel->setSeverity(PHUIInfoView::SEVERITY_NODATA);
$panel->appendChild(
phutil_tag(
'p',
array(
),
array(
phutil_tag('strong', array(), $title.': '),
$body,
)));
$this->minipanels[] = $panel;
}
public function buildAuditPanel() {
$request = $this->getRequest();
$user = $request->getUser();
$phids = PhabricatorAuditCommentEditor::loadAuditPHIDsForUser($user);
$query = id(new DiffusionCommitQuery())
->setViewer($user)
->withNeedsAuditByPHIDs($phids)
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_OPEN)
->needAuditRequests(true)
->needCommitData(true)
->setLimit(10);
$commits = $query->execute();
if (!$commits) {
return $this->renderMinipanel(
pht('No Audits'),
pht('No commits are waiting for you to audit them.'));
}
$view = id(new PhabricatorAuditListView())
->setCommits($commits)
->setUser($user);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
$title = pht('Audits');
$href = '/audit/';
$panel = new PHUIObjectBoxView();
$panel->setHeader($this->renderSectionHeader($title, $href));
$panel->setObjectList($view);
return $panel;
}
public function buildCommitPanel() {
$request = $this->getRequest();
$user = $request->getUser();
$phids = array($user->getPHID());
$query = id(new DiffusionCommitQuery())
->setViewer($user)
->withAuthorPHIDs($phids)
->withAuditStatus(DiffusionCommitQuery::AUDIT_STATUS_CONCERN)
->needCommitData(true)
->needAuditRequests(true)
->setLimit(10);
$commits = $query->execute();
if (!$commits) {
return $this->renderMinipanel(
pht('No Problem Commits'),
pht('No one has raised concerns with your commits.'));
}
$view = id(new PhabricatorAuditListView())
->setCommits($commits)
->setUser($user);
$phids = $view->getRequiredHandlePHIDs();
$handles = $this->loadViewerHandles($phids);
$view->setHandles($handles);
$title = pht('Problem Commits');
$href = '/audit/';
$panel = new PHUIObjectBoxView();
$panel->setHeader($this->renderSectionHeader($title, $href));
$panel->setObjectList($view);
return $panel;
}
}
diff --git a/src/applications/oauthserver/PhabricatorOAuthResponse.php b/src/applications/oauthserver/PhabricatorOAuthResponse.php
index 4e2c86c861..62c0fc9821 100644
--- a/src/applications/oauthserver/PhabricatorOAuthResponse.php
+++ b/src/applications/oauthserver/PhabricatorOAuthResponse.php
@@ -1,107 +1,106 @@
<?php
final class PhabricatorOAuthResponse extends AphrontResponse {
private $state;
private $content;
private $clientURI;
private $error;
private $errorDescription;
private function getState() {
return $this->state;
}
public function setState($state) {
$this->state = $state;
return $this;
}
private function getContent() {
return $this->content;
}
public function setContent($content) {
$this->content = $content;
return $this;
}
private function getClientURI() {
return $this->clientURI;
}
public function setClientURI(PhutilURI $uri) {
$this->setHTTPResponseCode(302);
$this->clientURI = $uri;
return $this;
}
private function getFullURI() {
$base_uri = $this->getClientURI();
$query_params = $this->buildResponseDict();
foreach ($query_params as $key => $value) {
$base_uri->setQueryParam($key, $value);
}
return $base_uri;
}
private function getError() {
return $this->error;
}
public function setError($error) {
// errors sometimes redirect to the client (302) but otherwise
// the spec says all code 400
if (!$this->getClientURI()) {
$this->setHTTPResponseCode(400);
}
$this->error = $error;
return $this;
}
private function getErrorDescription() {
return $this->errorDescription;
}
public function setErrorDescription($error_description) {
$this->errorDescription = $error_description;
return $this;
}
public function __construct() {
- $this->setHTTPResponseCode(200); // assume the best
- return $this;
+ $this->setHTTPResponseCode(200); // assume the best
}
public function getHeaders() {
$headers = array(
array('Content-Type', 'application/json'),
);
if ($this->getClientURI()) {
$headers[] = array('Location', $this->getFullURI());
}
// TODO -- T844 set headers with X-Auth-Scopes, etc
$headers = array_merge(parent::getHeaders(), $headers);
return $headers;
}
private function buildResponseDict() {
if ($this->getError()) {
$content = array(
'error' => $this->getError(),
'error_description' => $this->getErrorDescription(),
);
$this->setContent($content);
}
$content = $this->getContent();
if (!$content) {
return '';
}
if ($this->getState()) {
$content['state'] = $this->getState();
}
return $content;
}
public function buildResponseString() {
return $this->encodeJSONForHTTPResponse($this->buildResponseDict());
}
}
diff --git a/src/applications/oauthserver/PhabricatorOAuthServer.php b/src/applications/oauthserver/PhabricatorOAuthServer.php
index e1b9f516ba..38b2e34623 100644
--- a/src/applications/oauthserver/PhabricatorOAuthServer.php
+++ b/src/applications/oauthserver/PhabricatorOAuthServer.php
@@ -1,272 +1,272 @@
<?php
/**
* Implements core OAuth 2.0 Server logic.
*
* This class should be used behind business logic that parses input to
* determine pertinent @{class:PhabricatorUser} $user,
* @{class:PhabricatorOAuthServerClient} $client(s),
* @{class:PhabricatorOAuthServerAuthorizationCode} $code(s), and.
* @{class:PhabricatorOAuthServerAccessToken} $token(s).
*
* For an OAuth 2.0 server, there are two main steps:
*
* 1) Authorization - the user authorizes a given client to access the data
* the OAuth 2.0 server protects. Once this is achieved / if it has
* been achived already, the OAuth server sends the client an authorization
* code.
* 2) Access Token - the client should send the authorization code received in
* step 1 along with its id and secret to the OAuth server to receive an
* access token. This access token can later be used to access Phabricator
* data on behalf of the user.
*
* @task auth Authorizing @{class:PhabricatorOAuthServerClient}s and
* generating @{class:PhabricatorOAuthServerAuthorizationCode}s
* @task token Validating @{class:PhabricatorOAuthServerAuthorizationCode}s
* and generating @{class:PhabricatorOAuthServerAccessToken}s
* @task internal Internals
*/
final class PhabricatorOAuthServer extends Phobject {
const AUTHORIZATION_CODE_TIMEOUT = 300;
const ACCESS_TOKEN_TIMEOUT = 3600;
private $user;
private $client;
private function getUser() {
if (!$this->user) {
throw new PhutilInvalidStateException('setUser');
}
return $this->user;
}
public function setUser(PhabricatorUser $user) {
$this->user = $user;
return $this;
}
private function getClient() {
if (!$this->client) {
throw new PhutilInvalidStateException('setClient');
}
return $this->client;
}
public function setClient(PhabricatorOAuthServerClient $client) {
$this->client = $client;
return $this;
}
/**
* @task auth
* @return tuple <bool hasAuthorized, ClientAuthorization or null>
*/
public function userHasAuthorizedClient(array $scope) {
- $authorization = id(new PhabricatorOAuthClientAuthorization())->
- loadOneWhere(
+ $authorization = id(new PhabricatorOAuthClientAuthorization())
+ ->loadOneWhere(
'userPHID = %s AND clientPHID = %s',
$this->getUser()->getPHID(),
$this->getClient()->getPHID());
if (empty($authorization)) {
return array(false, null);
}
if ($scope) {
$missing_scope = array_diff_key($scope, $authorization->getScope());
} else {
$missing_scope = false;
}
if ($missing_scope) {
return array(false, $authorization);
}
return array(true, $authorization);
}
/**
* @task auth
*/
public function authorizeClient(array $scope) {
$authorization = new PhabricatorOAuthClientAuthorization();
$authorization->setUserPHID($this->getUser()->getPHID());
$authorization->setClientPHID($this->getClient()->getPHID());
$authorization->setScope($scope);
$authorization->save();
return $authorization;
}
/**
* @task auth
*/
public function generateAuthorizationCode(PhutilURI $redirect_uri) {
$code = Filesystem::readRandomCharacters(32);
$client = $this->getClient();
$authorization_code = new PhabricatorOAuthServerAuthorizationCode();
$authorization_code->setCode($code);
$authorization_code->setClientPHID($client->getPHID());
$authorization_code->setClientSecret($client->getSecret());
$authorization_code->setUserPHID($this->getUser()->getPHID());
$authorization_code->setRedirectURI((string)$redirect_uri);
$authorization_code->save();
return $authorization_code;
}
/**
* @task token
*/
public function generateAccessToken() {
$token = Filesystem::readRandomCharacters(32);
$access_token = new PhabricatorOAuthServerAccessToken();
$access_token->setToken($token);
$access_token->setUserPHID($this->getUser()->getPHID());
$access_token->setClientPHID($this->getClient()->getPHID());
$access_token->save();
return $access_token;
}
/**
* @task token
*/
public function validateAuthorizationCode(
PhabricatorOAuthServerAuthorizationCode $test_code,
PhabricatorOAuthServerAuthorizationCode $valid_code) {
// check that all the meta data matches
if ($test_code->getClientPHID() != $valid_code->getClientPHID()) {
return false;
}
if ($test_code->getClientSecret() != $valid_code->getClientSecret()) {
return false;
}
// check that the authorization code hasn't timed out
$created_time = $test_code->getDateCreated();
$must_be_used_by = $created_time + self::AUTHORIZATION_CODE_TIMEOUT;
return (time() < $must_be_used_by);
}
/**
* @task token
*/
public function validateAccessToken(
PhabricatorOAuthServerAccessToken $token,
$required_scope) {
$created_time = $token->getDateCreated();
$must_be_used_by = $created_time + self::ACCESS_TOKEN_TIMEOUT;
$expired = time() > $must_be_used_by;
$authorization = id(new PhabricatorOAuthClientAuthorization())
->loadOneWhere(
'userPHID = %s AND clientPHID = %s',
$token->getUserPHID(),
$token->getClientPHID());
if (!$authorization) {
return false;
}
$token_scope = $authorization->getScope();
if (!isset($token_scope[$required_scope])) {
return false;
}
$valid = true;
if ($expired) {
$valid = false;
// check if the scope includes "offline_access", which makes the
// token valid despite being expired
if (isset(
$token_scope[PhabricatorOAuthServerScope::SCOPE_OFFLINE_ACCESS])) {
$valid = true;
}
}
return $valid;
}
/**
* See http://tools.ietf.org/html/draft-ietf-oauth-v2-23#section-3.1.2
* for details on what makes a given redirect URI "valid".
*/
public function validateRedirectURI(PhutilURI $uri) {
if (!PhabricatorEnv::isValidRemoteURIForLink($uri)) {
return false;
}
if ($uri->getFragment()) {
return false;
}
if (!$uri->getDomain()) {
return false;
}
return true;
}
/**
* If there's a URI specified in an OAuth request, it must be validated in
* its own right. Further, it must have the same domain, the same path, the
* same port, and (at least) the same query parameters as the primary URI.
*/
public function validateSecondaryRedirectURI(
PhutilURI $secondary_uri,
PhutilURI $primary_uri) {
// The secondary URI must be valid.
if (!$this->validateRedirectURI($secondary_uri)) {
return false;
}
// Both URIs must point at the same domain.
if ($secondary_uri->getDomain() != $primary_uri->getDomain()) {
return false;
}
// Both URIs must have the same path
if ($secondary_uri->getPath() != $primary_uri->getPath()) {
return false;
}
// Both URIs must have the same port
if ($secondary_uri->getPort() != $primary_uri->getPort()) {
return false;
}
// Any query parameters present in the first URI must be exactly present
// in the second URI.
$need_params = $primary_uri->getQueryParams();
$have_params = $secondary_uri->getQueryParams();
foreach ($need_params as $key => $value) {
if (!array_key_exists($key, $have_params)) {
return false;
}
if ((string)$have_params[$key] != (string)$value) {
return false;
}
}
// If the first URI is HTTPS, the second URI must also be HTTPS. This
// defuses an attack where a third party with control over the network
// tricks you into using HTTP to authenticate over a link which is supposed
// to be HTTPS only and sniffs all your token cookies.
if (strtolower($primary_uri->getProtocol()) == 'https') {
if (strtolower($secondary_uri->getProtocol()) != 'https') {
return false;
}
}
return true;
}
}
diff --git a/src/applications/phame/controller/PhameController.php b/src/applications/phame/controller/PhameController.php
index 3a455b195a..20acd8f098 100644
--- a/src/applications/phame/controller/PhameController.php
+++ b/src/applications/phame/controller/PhameController.php
@@ -1,5 +1,3 @@
<?php
-abstract class PhameController extends PhabricatorController {
-
-}
+abstract class PhameController extends PhabricatorController {}
diff --git a/src/applications/releeph/storage/ReleephRequestTransaction.php b/src/applications/releeph/storage/ReleephRequestTransaction.php
index b12ed6ad8c..6c7f98b3db 100644
--- a/src/applications/releeph/storage/ReleephRequestTransaction.php
+++ b/src/applications/releeph/storage/ReleephRequestTransaction.php
@@ -1,275 +1,275 @@
<?php
final class ReleephRequestTransaction
extends PhabricatorApplicationTransaction {
const TYPE_REQUEST = 'releeph:request';
const TYPE_USER_INTENT = 'releeph:user_intent';
const TYPE_EDIT_FIELD = 'releeph:edit_field';
const TYPE_PICK_STATUS = 'releeph:pick_status';
const TYPE_COMMIT = 'releeph:commit';
const TYPE_DISCOVERY = 'releeph:discovery';
const TYPE_MANUAL_IN_BRANCH = 'releeph:manual';
public function getApplicationName() {
return 'releeph';
}
public function getApplicationTransactionType() {
return ReleephRequestPHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return new ReleephRequestTransactionComment();
}
public function hasChangeDetails() {
switch ($this->getTransactionType()) {
default;
break;
}
return parent::hasChangeDetails();
}
public function getRequiredHandlePHIDs() {
$phids = parent::getRequiredHandlePHIDs();
$phids[] = $this->getObjectPHID();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_REQUEST:
case self::TYPE_DISCOVERY:
$phids[] = $new;
break;
case self::TYPE_EDIT_FIELD:
self::searchForPHIDs($this->getOldValue(), $phids);
self::searchForPHIDs($this->getNewValue(), $phids);
break;
}
return $phids;
}
public function getTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$old = $this->getOldValue();
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_REQUEST:
return pht(
'%s requested %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
break;
case self::TYPE_USER_INTENT:
return $this->getIntentTitle();
break;
case self::TYPE_EDIT_FIELD:
$field = newv($this->getMetadataValue('fieldClass'), array());
$name = $field->getName();
$markup = $name;
if ($this->getRenderingTarget() ===
- PhabricatorApplicationTransaction::TARGET_HTML) {
+ parent::TARGET_HTML) {
$markup = hsprintf('<em>%s</em>', $name);
}
return pht(
'%s changed the %s to "%s"',
$this->renderHandleLink($author_phid),
$markup,
$field->normalizeForTransactionView($this, $new));
break;
case self::TYPE_PICK_STATUS:
switch ($new) {
case ReleephRequest::PICK_OK:
return pht('%s found this request picks without error',
$this->renderHandleLink($author_phid));
case ReleephRequest::REVERT_OK:
return pht('%s found this request reverts without error',
$this->renderHandleLink($author_phid));
case ReleephRequest::PICK_FAILED:
return pht("%s couldn't pick this request",
$this->renderHandleLink($author_phid));
case ReleephRequest::REVERT_FAILED:
return pht("%s couldn't revert this request",
$this->renderHandleLink($author_phid));
}
break;
case self::TYPE_COMMIT:
$action_type = $this->getMetadataValue('action');
switch ($action_type) {
case 'pick':
return pht(
'%s picked this request and committed the result upstream',
$this->renderHandleLink($author_phid));
break;
case 'revert':
return pht(
'%s reverted this request and committed the result upstream',
$this->renderHandleLink($author_phid));
break;
}
break;
case self::TYPE_MANUAL_IN_BRANCH:
$action = $new ? pht('picked') : pht('reverted');
return pht(
'%s marked this request as manually %s',
$this->renderHandleLink($author_phid),
$action);
break;
case self::TYPE_DISCOVERY:
return pht('%s discovered this commit as %s',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
break;
default:
return parent::getTitle();
break;
}
}
public function getActionName() {
switch ($this->getTransactionType()) {
case self::TYPE_REQUEST:
return pht('Requested');
case self::TYPE_COMMIT:
$action_type = $this->getMetadataValue('action');
switch ($action_type) {
case 'pick':
return pht('Picked');
case 'revert':
return pht('Reverted');
}
}
return parent::getActionName();
}
public function getColor() {
$new = $this->getNewValue();
switch ($this->getTransactionType()) {
case self::TYPE_USER_INTENT:
switch ($new) {
case ReleephRequest::INTENT_WANT:
return PhabricatorTransactions::COLOR_GREEN;
case ReleephRequest::INTENT_PASS:
return PhabricatorTransactions::COLOR_RED;
}
}
return parent::getColor();
}
private static function searchForPHIDs($thing, array &$phids) {
/**
* To implement something like getRequiredHandlePHIDs() in a
* ReleephFieldSpecification, we'd have to provide the field with its
* ReleephRequest (so that it could load the PHIDs from the
* ReleephRequest's storage, and return them.)
*
* We don't have fields initialized with their ReleephRequests, but we can
* make a good guess at what handles will be needed for rendering the field
* in this transaction by inspecting the old and new values.
*/
if (!is_array($thing)) {
$thing = array($thing);
}
foreach ($thing as $value) {
if (phid_get_type($value) !==
PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
$phids[] = $value;
}
}
}
private function getIntentTitle() {
$author_phid = $this->getAuthorPHID();
$object_phid = $this->getObjectPHID();
$new = $this->getNewValue();
$is_pusher = $this->getMetadataValue('isPusher');
switch ($new) {
case ReleephRequest::INTENT_WANT:
if ($is_pusher) {
return pht(
'%s approved this request',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s wanted this request',
$this->renderHandleLink($author_phid));
}
case ReleephRequest::INTENT_PASS:
if ($is_pusher) {
return pht(
'%s rejected this request',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s passed on this request',
$this->renderHandleLink($author_phid));
}
}
}
public function shouldHide() {
$type = $this->getTransactionType();
if ($type === self::TYPE_USER_INTENT &&
$this->getMetadataValue('isRQCreate')) {
return true;
}
if ($this->isBoringPickStatus()) {
return true;
}
// ReleephSummaryFieldSpecification is usually blank when an RQ is created,
// creating a transaction change from null to "". Hide these!
if ($type === self::TYPE_EDIT_FIELD) {
if ($this->getOldValue() === null && $this->getNewValue() === '') {
return true;
}
}
return parent::shouldHide();
}
public function isBoringPickStatus() {
$type = $this->getTransactionType();
if ($type === self::TYPE_PICK_STATUS) {
$new = $this->getNewValue();
if ($new === ReleephRequest::PICK_OK ||
$new === ReleephRequest::REVERT_OK) {
return true;
}
}
return false;
}
}
diff --git a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
index ac2957a4af..027e544776 100644
--- a/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
+++ b/src/infrastructure/lint/linter/PhabricatorJavelinLinter.php
@@ -1,290 +1,290 @@
<?php
final class PhabricatorJavelinLinter extends ArcanistLinter {
private $symbols = array();
private $symbolsBinary;
private $haveWarnedAboutBinary;
const LINT_PRIVATE_ACCESS = 1;
const LINT_MISSING_DEPENDENCY = 2;
const LINT_UNNECESSARY_DEPENDENCY = 3;
const LINT_UNKNOWN_DEPENDENCY = 4;
const LINT_MISSING_BINARY = 5;
public function getInfoName() {
return pht('Javelin Linter');
}
public function getInfoDescription() {
return pht(
'This linter is intended for use with the Javelin JS library and '.
'extensions. Use `%s` to run Javelin rules on Javascript source files.',
'javelinsymbols');
}
private function getBinaryPath() {
if ($this->symbolsBinary === null) {
list($err, $stdout) = exec_manual('which javelinsymbols');
$this->symbolsBinary = ($err ? false : rtrim($stdout));
}
return $this->symbolsBinary;
}
public function willLintPaths(array $paths) {
if (!$this->getBinaryPath()) {
return;
}
$root = dirname(phutil_get_library_root('phabricator'));
require_once $root.'/scripts/__init_script__.php';
$futures = array();
foreach ($paths as $path) {
if ($this->shouldIgnorePath($path)) {
continue;
}
$future = $this->newSymbolsFuture($path);
$futures[$path] = $future;
}
foreach (id(new FutureIterator($futures))->limit(8) as $path => $future) {
$this->symbols[$path] = $future->resolvex();
}
}
public function getLinterName() {
return 'JAVELIN';
}
public function getLinterConfigurationName() {
return 'javelin';
}
public function getLintSeverityMap() {
return array(
self::LINT_MISSING_BINARY => ArcanistLintSeverity::SEVERITY_WARNING,
);
}
public function getLintNameMap() {
return array(
self::LINT_PRIVATE_ACCESS =>
pht('Private Method/Member Access'),
self::LINT_MISSING_DEPENDENCY =>
pht('Missing Javelin Dependency'),
self::LINT_UNNECESSARY_DEPENDENCY =>
pht('Unnecessary Javelin Dependency'),
self::LINT_UNKNOWN_DEPENDENCY =>
pht('Unknown Javelin Dependency'),
self::LINT_MISSING_BINARY =>
pht('`%s` Not In Path', 'javelinsymbols'),
);
}
public function getCacheGranularity() {
- return ArcanistLinter::GRANULARITY_REPOSITORY;
+ return parent::GRANULARITY_REPOSITORY;
}
public function getCacheVersion() {
$version = '0';
$binary_path = $this->getBinaryPath();
if ($binary_path) {
$version .= '-'.md5_file($binary_path);
}
return $version;
}
private function shouldIgnorePath($path) {
return preg_match('@/__tests__/|externals/javelin/docs/@', $path);
}
public function lintPath($path) {
if ($this->shouldIgnorePath($path)) {
return;
}
if (!$this->symbolsBinary) {
if (!$this->haveWarnedAboutBinary) {
$this->haveWarnedAboutBinary = true;
// TODO: Write build documentation for the Javelin binaries and point
// the user at it.
$this->raiseLintAtLine(
1,
0,
self::LINT_MISSING_BINARY,
pht(
"The '%s' binary in the Javelin project is not available in %s, ".
"so the Javelin linter can't run. This isn't a big concern, ".
"but means some Javelin problems can't be automatically detected.",
'javelinsymbols',
'$PATH'));
}
return;
}
list($uses, $installs) = $this->getUsedAndInstalledSymbolsForPath($path);
foreach ($uses as $symbol => $line) {
$parts = explode('.', $symbol);
foreach ($parts as $part) {
if ($part[0] == '_' && $part[1] != '_') {
$base = implode('.', array_slice($parts, 0, 2));
if (!array_key_exists($base, $installs)) {
$this->raiseLintAtLine(
$line,
0,
self::LINT_PRIVATE_ACCESS,
pht(
"This file accesses private symbol '%s' across file ".
"boundaries. You may only access private members and methods ".
"from the file where they are defined.",
$symbol));
}
break;
}
}
}
$external_classes = array();
foreach ($uses as $symbol => $line) {
$parts = explode('.', $symbol);
$class = implode('.', array_slice($parts, 0, 2));
if (!array_key_exists($class, $external_classes) &&
!array_key_exists($class, $installs)) {
$external_classes[$class] = $line;
}
}
$celerity = CelerityResourceMap::getNamedInstance('phabricator');
$path = preg_replace(
'@^externals/javelinjs/src/@',
'webroot/rsrc/js/javelin/',
$path);
$need = $external_classes;
$resource_name = substr($path, strlen('webroot/'));
$requires = $celerity->getRequiredSymbolsForName($resource_name);
if (!$requires) {
$requires = array();
}
foreach ($requires as $key => $requires_symbol) {
$requires_name = $celerity->getResourceNameForSymbol($requires_symbol);
if ($requires_name === null) {
$this->raiseLintAtLine(
0,
0,
self::LINT_UNKNOWN_DEPENDENCY,
pht(
"This file %s component '%s', but it does not exist. ".
"You may need to rebuild the Celerity map.",
'@requires',
$requires_symbol));
unset($requires[$key]);
continue;
}
if (preg_match('/\\.css$/', $requires_name)) {
// If JS requires CSS, just assume everything is fine.
unset($requires[$key]);
} else {
$symbol_path = 'webroot/'.$requires_name;
list($ignored, $req_install) = $this->getUsedAndInstalledSymbolsForPath(
$symbol_path);
if (array_intersect_key($req_install, $external_classes)) {
$need = array_diff_key($need, $req_install);
unset($requires[$key]);
}
}
}
foreach ($need as $class => $line) {
$this->raiseLintAtLine(
$line,
0,
self::LINT_MISSING_DEPENDENCY,
pht(
"This file uses '%s' but does not @requires the component ".
"which installs it. You may need to rebuild the Celerity map.",
$class));
}
foreach ($requires as $component) {
$this->raiseLintAtLine(
0,
0,
self::LINT_UNNECESSARY_DEPENDENCY,
pht(
"This file %s component '%s' but does not use anything it provides.",
'@requires',
$component));
}
}
private function loadSymbols($path) {
if (empty($this->symbols[$path])) {
$this->symbols[$path] = $this->newSymbolsFuture($path)->resolvex();
}
return $this->symbols[$path];
}
private function newSymbolsFuture($path) {
$future = new ExecFuture('javelinsymbols # %s', $path);
$future->write($this->getData($path));
return $future;
}
private function getUsedAndInstalledSymbolsForPath($path) {
list($symbols) = $this->loadSymbols($path);
$symbols = trim($symbols);
$uses = array();
$installs = array();
if (empty($symbols)) {
// This file has no symbols.
return array($uses, $installs);
}
$symbols = explode("\n", trim($symbols));
foreach ($symbols as $line) {
$matches = null;
if (!preg_match('/^([?+\*])([^:]*):(\d+)$/', $line, $matches)) {
throw new Exception(
pht('Received malformed output from `%s`.', 'javelinsymbols'));
}
$type = $matches[1];
$symbol = $matches[2];
$line = $matches[3];
switch ($type) {
case '?':
$uses[$symbol] = $line;
break;
case '+':
$installs['JX.'.$symbol] = $line;
break;
}
}
$contents = $this->getData($path);
$matches = null;
$count = preg_match_all(
'/@javelin-installs\W+(\S+)/',
$contents,
$matches,
PREG_PATTERN_ORDER);
if ($count) {
foreach ($matches[1] as $symbol) {
$installs[$symbol] = 0;
}
}
return array($uses, $installs);
}
}
diff --git a/src/view/widget/bars/AphrontGlyphBarView.php b/src/view/widget/bars/AphrontGlyphBarView.php
index 5b8d606a27..b91a714b26 100644
--- a/src/view/widget/bars/AphrontGlyphBarView.php
+++ b/src/view/widget/bars/AphrontGlyphBarView.php
@@ -1,102 +1,102 @@
<?php
final class AphrontGlyphBarView extends AphrontBarView {
const BLACK_STAR = "\xE2\x98\x85";
const WHITE_STAR = "\xE2\x98\x86";
private $value;
private $max = 100;
private $numGlyphs = 5;
private $fgGlyph;
private $bgGlyph;
protected function getDefaultColor() {
- return AphrontBarView::COLOR_AUTO_GOODNESS;
+ return parent::COLOR_AUTO_GOODNESS;
}
public function setValue($value) {
$this->value = $value;
return $this;
}
public function setMax($max) {
$this->max = $max;
return $this;
}
public function setNumGlyphs($nn) {
$this->numGlyphs = $nn;
return $this;
}
public function setGlyph(PhutilSafeHTML $fg_glyph) {
$this->fgGlyph = $fg_glyph;
return $this;
}
public function setBackgroundGlyph(PhutilSafeHTML $bg_glyph) {
$this->bgGlyph = $bg_glyph;
return $this;
}
protected function getRatio() {
return min($this->value, $this->max) / $this->max;
}
public function render() {
require_celerity_resource('aphront-bars');
$ratio = $this->getRatio();
$percentage = 100 * $ratio;
$is_star = false;
if ($this->fgGlyph) {
$fg_glyph = $this->fgGlyph;
if ($this->bgGlyph) {
$bg_glyph = $this->bgGlyph;
} else {
$bg_glyph = $fg_glyph;
}
} else {
$is_star = true;
$fg_glyph = self::BLACK_STAR;
$bg_glyph = self::WHITE_STAR;
}
$fg_glyphs = array_fill(0, $this->numGlyphs, $fg_glyph);
$bg_glyphs = array_fill(0, $this->numGlyphs, $bg_glyph);
$color = $this->getColor();
return phutil_tag(
'div',
array(
'class' => "aphront-bar glyph color-{$color}",
),
array(
phutil_tag(
'div',
array(
'class' => 'glyphs'.($is_star ? ' starstar' : ''),
),
array(
phutil_tag(
'div',
array(
'class' => 'fg',
'style' => "width: {$percentage}%;",
),
$fg_glyphs),
phutil_tag(
'div',
array(),
$bg_glyphs),
)),
phutil_tag(
'div',
array('class' => 'caption'),
$this->getCaption()),
));
}
}
diff --git a/src/view/widget/bars/AphrontProgressBarView.php b/src/view/widget/bars/AphrontProgressBarView.php
index 7117435aa5..0a967a4fa5 100644
--- a/src/view/widget/bars/AphrontProgressBarView.php
+++ b/src/view/widget/bars/AphrontProgressBarView.php
@@ -1,58 +1,58 @@
<?php
final class AphrontProgressBarView extends AphrontBarView {
const WIDTH = 100;
private $value;
private $max = 100;
private $alt = '';
protected function getDefaultColor() {
- return AphrontBarView::COLOR_AUTO_BADNESS;
+ return parent::COLOR_AUTO_BADNESS;
}
public function setValue($value) {
$this->value = $value;
return $this;
}
public function setMax($max) {
$this->max = $max;
return $this;
}
public function setAlt($text) {
$this->alt = $text;
return $this;
}
protected function getRatio() {
return min($this->value, $this->max) / $this->max;
}
public function render() {
require_celerity_resource('aphront-bars');
$ratio = $this->getRatio();
$width = self::WIDTH * $ratio;
$color = $this->getColor();
return phutil_tag_div(
"aphront-bar progress color-{$color}",
array(
phutil_tag(
'div',
array('title' => $this->alt),
phutil_tag(
'div',
array('style' => "width: {$width}px;"),
'')),
phutil_tag(
'span',
array(),
$this->getCaption()),
));
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jun 10, 11:46 PM (23 h, 30 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
141374
Default Alt Text
(61 KB)

Event Timeline