Find this useful? Enter your email to receive occasional updates for securing PHP code.
Signing you up...
Thank you for signing up!
PHP Decode
<?php final class HeraldTranscriptController extends HeraldController { private $handl..
Decoded Output download
<?php
final class HeraldTranscriptController extends HeraldController {
private $handles;
private $adapter;
private function getAdapter() {
return $this->adapter;
}
public function buildApplicationMenu() {
// Use the menu we build in this controller, not the default menu for
// Herald.
return null;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$xscript = id(new HeraldTranscriptQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$xscript) {
return new Aphront404Response();
}
$view_key = $this->getViewKey($request);
if (!$view_key) {
return new Aphront404Response();
}
$navigation = $this->newSideNavView($xscript, $view_key);
$object = $xscript->getObject();
require_celerity_resource('herald-test-css');
$content = array();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
$notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setTitle(pht('Old Transcript'))
->appendChild(phutil_tag(
'p',
array(),
pht('Details of this transcript have been garbage collected.')));
$content[] = $notice;
} else {
$map = HeraldAdapter::getEnabledAdapterMap($viewer);
$object_type = $object_xscript->getType();
if (empty($map[$object_type])) {
// TODO: We should filter these out in the Query, but we have to load
// the objectTranscript right now, which is potentially enormous. We
// should denormalize the object type, or move the data into a separate
// table, and then filter this earlier (and thus raise a better error).
// For now, just block access so we don't violate policies.
throw new Exception(
pht('This transcript has an invalid or inaccessible adapter.'));
}
$this->adapter = HeraldAdapter::getAdapterForContentType($object_type);
$phids = $this->getTranscriptPHIDs($xscript);
$phids = array_unique($phids);
$phids = array_filter($phids);
$handles = $this->loadViewerHandles($phids);
$this->handles = $handles;
$warning_panel = $this->buildWarningPanel($xscript);
$content[] = $warning_panel;
$content[] = $this->newContentView($xscript, $view_key);
}
$crumbs = id($this->buildApplicationCrumbs())
->addTextCrumb(
pht('Transcripts'),
$this->getApplicationURI('/transcript/'))
->addTextCrumb(pht('Transcript %d', $xscript->getID()))
->setBorder(true);
$title = pht('Herald Transcript %s', $xscript->getID());
$header = $this->newHeaderView($xscript, $title);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($content);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
protected function renderConditionTestValue($condition, $handles) {
// TODO: This is all a hacky mess and should be driven through FieldValue
// eventually.
switch ($condition->getFieldName()) {
case HeraldAnotherRuleField::FIELDCONST:
$value = array($condition->getTestValue());
break;
default:
$value = $condition->getTestValue();
break;
}
if (!is_scalar($value) && $value !== null) {
foreach ($value as $key => $phid) {
$handle = idx($handles, $phid);
if ($handle && $handle->isComplete()) {
$value[$key] = $handle->getName();
} else {
// This happens for things like task priorities, statuses, and
// custom fields.
$value[$key] = $phid;
}
}
sort($value);
$value = implode(', ', $value);
}
return phutil_tag('span', array('class' => 'condition-test-value'), $value);
}
protected function getTranscriptPHIDs($xscript) {
$phids = array();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
return array();
}
$phids[] = $object_xscript->getPHID();
foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
// TODO: This is total hacks. Add another amazing layer of abstraction.
$target = (array)$apply_xscript->getTarget();
foreach ($target as $phid) {
if ($phid) {
$phids[] = $phid;
}
}
}
foreach ($xscript->getRuleTranscripts() as $rule_xscript) {
$phids[] = $rule_xscript->getRuleOwner();
}
$condition_xscripts = $xscript->getConditionTranscripts();
if ($condition_xscripts) {
$condition_xscripts = call_user_func_array(
'array_merge',
$condition_xscripts);
}
foreach ($condition_xscripts as $condition_xscript) {
switch ($condition_xscript->getFieldName()) {
case HeraldAnotherRuleField::FIELDCONST:
$phids[] = $condition_xscript->getTestValue();
break;
default:
$value = $condition_xscript->getTestValue();
// TODO: Also total hacks.
if (is_array($value)) {
foreach ($value as $phid) {
if ($phid) {
// TODO: Probably need to make sure this
// "looks like" a PHID or decrease the level of hacks here;
// this used to be an is_numeric() check in Facebook land.
$phids[] = $phid;
}
}
}
break;
}
}
return $phids;
}
private function buildWarningPanel(HeraldTranscript $xscript) {
$request = $this->getRequest();
$panel = null;
if ($xscript->getObjectTranscript()) {
$handles = $this->handles;
$object_xscript = $xscript->getObjectTranscript();
$handle = $handles[$object_xscript->getPHID()];
if ($handle->getType() ==
PhabricatorRepositoryCommitPHIDType::TYPECONST) {
$commit = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withPHIDs(array($handle->getPHID()))
->executeOne();
if ($commit) {
$repository = $commit->getRepository();
if ($repository->isImporting()) {
$title = pht(
'The %s repository is still importing.',
$repository->getMonogram());
$body = pht(
'Herald rules will not trigger until import completes.');
} else if (!$repository->isTracked()) {
$title = pht(
'The %s repository is not tracked.',
$repository->getMonogram());
$body = pht(
'Herald rules will not trigger until tracking is enabled.');
} else {
return $panel;
}
$panel = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle($title)
->appendChild($body);
}
}
}
return $panel;
}
private function buildActionTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$action_xscript = mgroup($xscript->getApplyTranscripts(), 'getRuleID');
$adapter = $this->getAdapter();
$field_names = $adapter->getFieldNameMap();
$condition_names = $adapter->getConditionNameMap();
$handles = $this->handles;
$action_map = $xscript->getApplyTranscripts();
$action_map = mgroup($action_map, 'getRuleID');
$rule_list = id(new PHUIObjectItemListView())
->setNoDataString(pht('No Herald rules applied to this object.'))
->setFlush(true);
$rule_xscripts = $xscript->getRuleTranscripts();
$rule_xscripts = msort($rule_xscripts, 'getRuleID');
foreach ($rule_xscripts as $rule_xscript) {
$rule_id = $rule_xscript->getRuleID();
$rule_monogram = pht('H%d', $rule_id);
$rule_uri = '/'.$rule_monogram;
$rule_item = id(new PHUIObjectItemView())
->setObjectName($rule_monogram)
->setHeader($rule_xscript->getRuleName())
->setHref($rule_uri);
$rule_result = $rule_xscript->getRuleResult();
if (!$rule_result->getShouldApplyActions()) {
$rule_item->setDisabled(true);
}
$rule_list->addItem($rule_item);
// Build the field/condition transcript.
$cond_xscripts = $xscript->getConditionTranscriptsForRule($rule_id);
$cond_list = id(new PHUIStatusListView());
$cond_list->addItem(
id(new PHUIStatusItemView())
->setTarget(phutil_tag('strong', array(), pht('Conditions'))));
foreach ($cond_xscripts as $cond_xscript) {
$result = $cond_xscript->getResult();
$icon = $result->getIconIcon();
$color = $result->getIconColor();
$name = $result->getName();
$result_details = $result->newDetailsView($viewer);
if ($result_details !== null) {
$result_details = phutil_tag(
'div',
array(
'class' => 'herald-condition-note',
),
$result_details);
}
// TODO: This is not really translatable and should be driven through
// HeraldField.
$explanation = pht(
'%s %s %s',
idx($field_names, $cond_xscript->getFieldName(), pht('Unknown')),
idx($condition_names, $cond_xscript->getCondition(), pht('Unknown')),
$this->renderConditionTestValue($cond_xscript, $handles));
$cond_item = id(new PHUIStatusItemView())
->setIcon($icon, $color)
->setTarget($name)
->setNote(array($explanation, $result_details));
$cond_list->addItem($cond_item);
}
$rule_result = $rule_xscript->getRuleResult();
$last_icon = $rule_result->getIconIcon();
$last_color = $rule_result->getIconColor();
$last_result = $rule_result->getName();
$last_note = $rule_result->getDescription();
$last_details = $rule_result->newDetailsView($viewer);
if ($last_details !== null) {
$last_details = phutil_tag(
'div',
array(
'class' => 'herald-condition-note',
),
$last_details);
}
$cond_last = id(new PHUIStatusItemView())
->setIcon($last_icon, $last_color)
->setTarget(phutil_tag('strong', array(), $last_result))
->setNote(array($last_note, $last_details));
$cond_list->addItem($cond_last);
$cond_box = id(new PHUIBoxView())
->appendChild($cond_list)
->addMargin(PHUI::MARGIN_LARGE_LEFT);
$rule_item->appendChild($cond_box);
// Not all rules will have any action transcripts, but we show them
// in general because they may have relevant information even when
// rules did not take actions. In particular, state-based actions may
// forbid rules from matching.
$cond_box->addMargin(PHUI::MARGIN_MEDIUM_BOTTOM);
$action_xscripts = idx($action_map, $rule_id, array());
foreach ($action_xscripts as $action_xscript) {
$action_key = $action_xscript->getAction();
$action = $adapter->getActionImplementation($action_key);
if ($action) {
$name = $action->getHeraldActionName();
$action->setViewer($this->getViewer());
} else {
$name = pht('Unknown Action ("%s")', $action_key);
}
$name = pht('Action: %s', $name);
$action_list = id(new PHUIStatusListView());
$action_list->addItem(
id(new PHUIStatusItemView())
->setTarget(phutil_tag('strong', array(), $name)));
$action_box = id(new PHUIBoxView())
->appendChild($action_list)
->addMargin(PHUI::MARGIN_LARGE_LEFT);
$rule_item->appendChild($action_box);
$log = $action_xscript->getAppliedReason();
// Handle older transcripts which used a static string to record
// action results.
if ($xscript->getDryRun()) {
$action_list->addItem(
id(new PHUIStatusItemView())
->setIcon('fa-ban', 'grey')
->setTarget(pht('Dry Run'))
->setNote(
pht(
'This was a dry run, so no actions were taken.')));
continue;
} else if (!is_array($log)) {
$action_list->addItem(
id(new PHUIStatusItemView())
->setIcon('fa-clock-o', 'grey')
->setTarget(pht('Old Transcript'))
->setNote(
pht(
'This is an old transcript which uses an obsolete log '.
'format. Detailed action information is not available.')));
continue;
}
foreach ($log as $entry) {
$type = idx($entry, 'type');
$data = idx($entry, 'data');
if ($action) {
$icon = $action->renderActionEffectIcon($type, $data);
$color = $action->renderActionEffectColor($type, $data);
$name = $action->renderActionEffectName($type, $data);
$note = $action->renderEffectDescription($type, $data);
} else {
$icon = 'fa-question-circle';
$color = 'indigo';
$name = pht('Unknown Effect ("%s")', $type);
$note = null;
}
$action_item = id(new PHUIStatusItemView())
->setIcon($icon, $color)
->setTarget($name)
->setNote($note);
$action_list->addItem($action_item);
}
}
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Rule Transcript'))
->appendChild($rule_list);
$content = array();
if ($xscript->getDryRun()) {
$notice = new PHUIInfoView();
$notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$notice->setTitle(pht('Dry Run'));
$notice->appendChild(
pht(
'This was a dry run to test Herald rules, '.
'no actions were executed.'));
$content[] = $notice;
}
$content[] = $box;
return $content;
}
private function buildObjectTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$adapter = $this->getAdapter();
$field_names = $adapter->getFieldNameMap();
$object_xscript = $xscript->getObjectTranscript();
$rows = array();
if ($object_xscript) {
$phid = $object_xscript->getPHID();
$handles = $this->handles;
$rows[] = array(
pht('Object Name'),
$object_xscript->getName(),
);
$rows[] = array(
pht('Object Type'),
$object_xscript->getType(),
);
$rows[] = array(
pht('Object PHID'),
$phid,
);
$rows[] = array(
pht('Object Link'),
$handles[$phid]->renderLink(),
);
}
foreach ($xscript->getMetadataMap() as $key => $value) {
$rows[] = array(
$key,
$value,
);
}
if ($object_xscript) {
foreach ($object_xscript->getFields() as $field_type => $value) {
if (isset($field_names[$field_type])) {
$field_name = pht('Field: %s', $field_names[$field_type]);
} else {
$field_name = pht('Unknown Field ("%s")', $field_type);
}
$field_value = $adapter->renderFieldTranscriptValue(
$viewer,
$field_type,
$value);
$rows[] = array(
$field_name,
$field_value,
);
}
}
$property_list = new PHUIPropertyListView();
$property_list->setStacked(true);
foreach ($rows as $row) {
$property_list->addProperty($row[0], $row[1]);
}
$box = new PHUIObjectBoxView();
$box->setHeaderText(pht('Object Transcript'));
$box->appendChild($property_list);
return $box;
}
private function buildTransactionsTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$xaction_phids = $this->getTranscriptTransactionPHIDs($xscript);
if ($xaction_phids) {
$object = $xscript->getObject();
$query = PhabricatorApplicationTransactionQuery::newQueryForObject(
$object);
$xactions = $query
->setViewer($viewer)
->withPHIDs($xaction_phids)
->execute();
$xactions = mpull($xactions, null, 'getPHID');
} else {
$xactions = array();
}
$rows = array();
foreach ($xaction_phids as $xaction_phid) {
$xaction = idx($xactions, $xaction_phid);
$xaction_identifier = $xaction_phid;
$xaction_date = null;
$xaction_display = null;
if ($xaction) {
$xaction_identifier = $xaction->getID();
$xaction_date = phabricator_datetime(
$xaction->getDateCreated(),
$viewer);
// Since we don't usually render transactions outside of the context
// of objects, some of them might depend on missing object data. Out of
// an abundance of caution, catch any rendering issues.
try {
$xaction_display = $xaction->getTitle();
} catch (Exception $ex) {
$xaction_display = $ex->getMessage();
}
}
$rows[] = array(
$xaction_identifier,
$xaction_display,
$xaction_date,
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('ID'),
pht('Transaction'),
pht('Date'),
))
->setColumnClasses(
array(
null,
'wide',
null,
));
$box_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Transactions'))
->setTable($table_view);
return $box_view;
}
private function buildProfilerTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$object_xscript = $xscript->getObjectTranscript();
$profile = $object_xscript->getProfile();
// If this is an older transcript without profiler information, don't
// show anything.
if ($profile === null) {
return null;
}
$profile = isort($profile, 'elapsed');
$profile = array_reverse($profile);
$phids = array();
foreach ($profile as $frame) {
if ($frame['type'] === 'rule') {
$phids[] = $frame['key'];
}
}
$handles = $viewer->loadHandles($phids);
$field_map = HeraldField::getAllFields();
$rows = array();
foreach ($profile as $frame) {
$cost = $frame['elapsed'];
$cost = 1000000 * $cost;
$cost = pht('%sus', new PhutilNumber($cost));
$type = $frame['type'];
switch ($type) {
case 'rule':
$type_display = pht('Rule');
break;
case 'field':
$type_display = pht('Field');
break;
default:
$type_display = $type;
break;
}
$key = $frame['key'];
switch ($type) {
case 'field':
$field_object = idx($field_map, $key);
if ($field_object) {
$key_display = $field_object->getHeraldFieldName();
} else {
$key_display = $key;
}
break;
case 'rule':
$key_display = $handles[$key]->renderLink();
break;
default:
$key_display = $key;
break;
}
$rows[] = array(
$type_display,
$key_display,
$cost,
pht('%s', new PhutilNumber($frame['count'])),
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Type'),
pht('What'),
pht('Cost'),
pht('Count'),
))
->setColumnClasses(
array(
null,
'wide',
'right',
'right',
));
$box_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Profile'))
->setTable($table_view);
return $box_view;
}
private function getViewKey(AphrontRequest $request) {
$view_key = $request->getURIData('view');
if ($view_key === null) {
return 'rules';
}
switch ($view_key) {
case 'fields':
case 'xactions':
case 'profile':
return $view_key;
default:
return null;
}
}
private function newSideNavView(
HeraldTranscript $xscript,
$view_key) {
$base_uri = urisprintf(
'transcript/%d/',
$xscript->getID());
$base_uri = $this->getApplicationURI($base_uri);
$base_uri = new PhutilURI($base_uri);
$nav = id(new AphrontSideNavFilterView())
->setBaseURI($base_uri);
$nav->newLink('rules')
->setHref($base_uri)
->setName(pht('Rules'))
->setIcon('fa-list-ul');
$nav->newLink('fields')
->setName(pht('Field Values'))
->setIcon('fa-file-text-o');
$xaction_phids = $this->getTranscriptTransactionPHIDs($xscript);
$has_xactions = (bool)$xaction_phids;
$nav->newLink('xactions')
->setName(pht('Transactions'))
->setIcon('fa-forward')
->setDisabled(!$has_xactions);
$nav->newLink('profile')
->setName(pht('Profiler'))
->setIcon('fa-tachometer');
$nav->selectFilter($view_key);
return $nav;
}
private function newContentView(
HeraldTranscript $xscript,
$view_key) {
switch ($view_key) {
case 'rules':
$content = $this->buildActionTranscriptPanel($xscript);
break;
case 'fields':
$content = $this->buildObjectTranscriptPanel($xscript);
break;
case 'xactions':
$content = $this->buildTransactionsTranscriptPanel($xscript);
break;
case 'profile':
$content = $this->buildProfilerTranscriptPanel($xscript);
break;
default:
throw new Exception(pht('Unknown view key "%s".', $view_key));
}
return $content;
}
private function getTranscriptTransactionPHIDs(HeraldTranscript $xscript) {
$object_xscript = $xscript->getObjectTranscript();
$xaction_phids = $object_xscript->getAppliedTransactionPHIDs();
// If the value is "null", this is an older transcript or this adapter
// does not use transactions.
//
// (If the value is "array()", this is a modern transcript which uses
// transactions, there just weren't any applied.)
if ($xaction_phids === null) {
return array();
}
$object = $xscript->getObject();
// If this object doesn't implement the right interface, we won't be
// able to load the transactions.
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
return array();
}
return $xaction_phids;
}
private function newHeaderView(HeraldTranscript $xscript, $title) {
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-list-ul');
if ($xscript->getDryRun()) {
$dry_run_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_VIOLET)
->setName(pht('Dry Run'))
->setIcon('fa-exclamation-triangle');
$header->addTag($dry_run_tag);
}
return $header;
}
}
?>
Did this file decode correctly?
Original Code
<?php
final class HeraldTranscriptController extends HeraldController {
private $handles;
private $adapter;
private function getAdapter() {
return $this->adapter;
}
public function buildApplicationMenu() {
// Use the menu we build in this controller, not the default menu for
// Herald.
return null;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$xscript = id(new HeraldTranscriptQuery())
->setViewer($viewer)
->withIDs(array($request->getURIData('id')))
->executeOne();
if (!$xscript) {
return new Aphront404Response();
}
$view_key = $this->getViewKey($request);
if (!$view_key) {
return new Aphront404Response();
}
$navigation = $this->newSideNavView($xscript, $view_key);
$object = $xscript->getObject();
require_celerity_resource('herald-test-css');
$content = array();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
$notice = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->setTitle(pht('Old Transcript'))
->appendChild(phutil_tag(
'p',
array(),
pht('Details of this transcript have been garbage collected.')));
$content[] = $notice;
} else {
$map = HeraldAdapter::getEnabledAdapterMap($viewer);
$object_type = $object_xscript->getType();
if (empty($map[$object_type])) {
// TODO: We should filter these out in the Query, but we have to load
// the objectTranscript right now, which is potentially enormous. We
// should denormalize the object type, or move the data into a separate
// table, and then filter this earlier (and thus raise a better error).
// For now, just block access so we don't violate policies.
throw new Exception(
pht('This transcript has an invalid or inaccessible adapter.'));
}
$this->adapter = HeraldAdapter::getAdapterForContentType($object_type);
$phids = $this->getTranscriptPHIDs($xscript);
$phids = array_unique($phids);
$phids = array_filter($phids);
$handles = $this->loadViewerHandles($phids);
$this->handles = $handles;
$warning_panel = $this->buildWarningPanel($xscript);
$content[] = $warning_panel;
$content[] = $this->newContentView($xscript, $view_key);
}
$crumbs = id($this->buildApplicationCrumbs())
->addTextCrumb(
pht('Transcripts'),
$this->getApplicationURI('/transcript/'))
->addTextCrumb(pht('Transcript %d', $xscript->getID()))
->setBorder(true);
$title = pht('Herald Transcript %s', $xscript->getID());
$header = $this->newHeaderView($xscript, $title);
$view = id(new PHUITwoColumnView())
->setHeader($header)
->setFooter($content);
return $this->newPage()
->setTitle($title)
->setCrumbs($crumbs)
->setNavigation($navigation)
->appendChild($view);
}
protected function renderConditionTestValue($condition, $handles) {
// TODO: This is all a hacky mess and should be driven through FieldValue
// eventually.
switch ($condition->getFieldName()) {
case HeraldAnotherRuleField::FIELDCONST:
$value = array($condition->getTestValue());
break;
default:
$value = $condition->getTestValue();
break;
}
if (!is_scalar($value) && $value !== null) {
foreach ($value as $key => $phid) {
$handle = idx($handles, $phid);
if ($handle && $handle->isComplete()) {
$value[$key] = $handle->getName();
} else {
// This happens for things like task priorities, statuses, and
// custom fields.
$value[$key] = $phid;
}
}
sort($value);
$value = implode(', ', $value);
}
return phutil_tag('span', array('class' => 'condition-test-value'), $value);
}
protected function getTranscriptPHIDs($xscript) {
$phids = array();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
return array();
}
$phids[] = $object_xscript->getPHID();
foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
// TODO: This is total hacks. Add another amazing layer of abstraction.
$target = (array)$apply_xscript->getTarget();
foreach ($target as $phid) {
if ($phid) {
$phids[] = $phid;
}
}
}
foreach ($xscript->getRuleTranscripts() as $rule_xscript) {
$phids[] = $rule_xscript->getRuleOwner();
}
$condition_xscripts = $xscript->getConditionTranscripts();
if ($condition_xscripts) {
$condition_xscripts = call_user_func_array(
'array_merge',
$condition_xscripts);
}
foreach ($condition_xscripts as $condition_xscript) {
switch ($condition_xscript->getFieldName()) {
case HeraldAnotherRuleField::FIELDCONST:
$phids[] = $condition_xscript->getTestValue();
break;
default:
$value = $condition_xscript->getTestValue();
// TODO: Also total hacks.
if (is_array($value)) {
foreach ($value as $phid) {
if ($phid) {
// TODO: Probably need to make sure this
// "looks like" a PHID or decrease the level of hacks here;
// this used to be an is_numeric() check in Facebook land.
$phids[] = $phid;
}
}
}
break;
}
}
return $phids;
}
private function buildWarningPanel(HeraldTranscript $xscript) {
$request = $this->getRequest();
$panel = null;
if ($xscript->getObjectTranscript()) {
$handles = $this->handles;
$object_xscript = $xscript->getObjectTranscript();
$handle = $handles[$object_xscript->getPHID()];
if ($handle->getType() ==
PhabricatorRepositoryCommitPHIDType::TYPECONST) {
$commit = id(new DiffusionCommitQuery())
->setViewer($request->getUser())
->withPHIDs(array($handle->getPHID()))
->executeOne();
if ($commit) {
$repository = $commit->getRepository();
if ($repository->isImporting()) {
$title = pht(
'The %s repository is still importing.',
$repository->getMonogram());
$body = pht(
'Herald rules will not trigger until import completes.');
} else if (!$repository->isTracked()) {
$title = pht(
'The %s repository is not tracked.',
$repository->getMonogram());
$body = pht(
'Herald rules will not trigger until tracking is enabled.');
} else {
return $panel;
}
$panel = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_WARNING)
->setTitle($title)
->appendChild($body);
}
}
}
return $panel;
}
private function buildActionTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$action_xscript = mgroup($xscript->getApplyTranscripts(), 'getRuleID');
$adapter = $this->getAdapter();
$field_names = $adapter->getFieldNameMap();
$condition_names = $adapter->getConditionNameMap();
$handles = $this->handles;
$action_map = $xscript->getApplyTranscripts();
$action_map = mgroup($action_map, 'getRuleID');
$rule_list = id(new PHUIObjectItemListView())
->setNoDataString(pht('No Herald rules applied to this object.'))
->setFlush(true);
$rule_xscripts = $xscript->getRuleTranscripts();
$rule_xscripts = msort($rule_xscripts, 'getRuleID');
foreach ($rule_xscripts as $rule_xscript) {
$rule_id = $rule_xscript->getRuleID();
$rule_monogram = pht('H%d', $rule_id);
$rule_uri = '/'.$rule_monogram;
$rule_item = id(new PHUIObjectItemView())
->setObjectName($rule_monogram)
->setHeader($rule_xscript->getRuleName())
->setHref($rule_uri);
$rule_result = $rule_xscript->getRuleResult();
if (!$rule_result->getShouldApplyActions()) {
$rule_item->setDisabled(true);
}
$rule_list->addItem($rule_item);
// Build the field/condition transcript.
$cond_xscripts = $xscript->getConditionTranscriptsForRule($rule_id);
$cond_list = id(new PHUIStatusListView());
$cond_list->addItem(
id(new PHUIStatusItemView())
->setTarget(phutil_tag('strong', array(), pht('Conditions'))));
foreach ($cond_xscripts as $cond_xscript) {
$result = $cond_xscript->getResult();
$icon = $result->getIconIcon();
$color = $result->getIconColor();
$name = $result->getName();
$result_details = $result->newDetailsView($viewer);
if ($result_details !== null) {
$result_details = phutil_tag(
'div',
array(
'class' => 'herald-condition-note',
),
$result_details);
}
// TODO: This is not really translatable and should be driven through
// HeraldField.
$explanation = pht(
'%s %s %s',
idx($field_names, $cond_xscript->getFieldName(), pht('Unknown')),
idx($condition_names, $cond_xscript->getCondition(), pht('Unknown')),
$this->renderConditionTestValue($cond_xscript, $handles));
$cond_item = id(new PHUIStatusItemView())
->setIcon($icon, $color)
->setTarget($name)
->setNote(array($explanation, $result_details));
$cond_list->addItem($cond_item);
}
$rule_result = $rule_xscript->getRuleResult();
$last_icon = $rule_result->getIconIcon();
$last_color = $rule_result->getIconColor();
$last_result = $rule_result->getName();
$last_note = $rule_result->getDescription();
$last_details = $rule_result->newDetailsView($viewer);
if ($last_details !== null) {
$last_details = phutil_tag(
'div',
array(
'class' => 'herald-condition-note',
),
$last_details);
}
$cond_last = id(new PHUIStatusItemView())
->setIcon($last_icon, $last_color)
->setTarget(phutil_tag('strong', array(), $last_result))
->setNote(array($last_note, $last_details));
$cond_list->addItem($cond_last);
$cond_box = id(new PHUIBoxView())
->appendChild($cond_list)
->addMargin(PHUI::MARGIN_LARGE_LEFT);
$rule_item->appendChild($cond_box);
// Not all rules will have any action transcripts, but we show them
// in general because they may have relevant information even when
// rules did not take actions. In particular, state-based actions may
// forbid rules from matching.
$cond_box->addMargin(PHUI::MARGIN_MEDIUM_BOTTOM);
$action_xscripts = idx($action_map, $rule_id, array());
foreach ($action_xscripts as $action_xscript) {
$action_key = $action_xscript->getAction();
$action = $adapter->getActionImplementation($action_key);
if ($action) {
$name = $action->getHeraldActionName();
$action->setViewer($this->getViewer());
} else {
$name = pht('Unknown Action ("%s")', $action_key);
}
$name = pht('Action: %s', $name);
$action_list = id(new PHUIStatusListView());
$action_list->addItem(
id(new PHUIStatusItemView())
->setTarget(phutil_tag('strong', array(), $name)));
$action_box = id(new PHUIBoxView())
->appendChild($action_list)
->addMargin(PHUI::MARGIN_LARGE_LEFT);
$rule_item->appendChild($action_box);
$log = $action_xscript->getAppliedReason();
// Handle older transcripts which used a static string to record
// action results.
if ($xscript->getDryRun()) {
$action_list->addItem(
id(new PHUIStatusItemView())
->setIcon('fa-ban', 'grey')
->setTarget(pht('Dry Run'))
->setNote(
pht(
'This was a dry run, so no actions were taken.')));
continue;
} else if (!is_array($log)) {
$action_list->addItem(
id(new PHUIStatusItemView())
->setIcon('fa-clock-o', 'grey')
->setTarget(pht('Old Transcript'))
->setNote(
pht(
'This is an old transcript which uses an obsolete log '.
'format. Detailed action information is not available.')));
continue;
}
foreach ($log as $entry) {
$type = idx($entry, 'type');
$data = idx($entry, 'data');
if ($action) {
$icon = $action->renderActionEffectIcon($type, $data);
$color = $action->renderActionEffectColor($type, $data);
$name = $action->renderActionEffectName($type, $data);
$note = $action->renderEffectDescription($type, $data);
} else {
$icon = 'fa-question-circle';
$color = 'indigo';
$name = pht('Unknown Effect ("%s")', $type);
$note = null;
}
$action_item = id(new PHUIStatusItemView())
->setIcon($icon, $color)
->setTarget($name)
->setNote($note);
$action_list->addItem($action_item);
}
}
}
$box = id(new PHUIObjectBoxView())
->setHeaderText(pht('Rule Transcript'))
->appendChild($rule_list);
$content = array();
if ($xscript->getDryRun()) {
$notice = new PHUIInfoView();
$notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
$notice->setTitle(pht('Dry Run'));
$notice->appendChild(
pht(
'This was a dry run to test Herald rules, '.
'no actions were executed.'));
$content[] = $notice;
}
$content[] = $box;
return $content;
}
private function buildObjectTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$adapter = $this->getAdapter();
$field_names = $adapter->getFieldNameMap();
$object_xscript = $xscript->getObjectTranscript();
$rows = array();
if ($object_xscript) {
$phid = $object_xscript->getPHID();
$handles = $this->handles;
$rows[] = array(
pht('Object Name'),
$object_xscript->getName(),
);
$rows[] = array(
pht('Object Type'),
$object_xscript->getType(),
);
$rows[] = array(
pht('Object PHID'),
$phid,
);
$rows[] = array(
pht('Object Link'),
$handles[$phid]->renderLink(),
);
}
foreach ($xscript->getMetadataMap() as $key => $value) {
$rows[] = array(
$key,
$value,
);
}
if ($object_xscript) {
foreach ($object_xscript->getFields() as $field_type => $value) {
if (isset($field_names[$field_type])) {
$field_name = pht('Field: %s', $field_names[$field_type]);
} else {
$field_name = pht('Unknown Field ("%s")', $field_type);
}
$field_value = $adapter->renderFieldTranscriptValue(
$viewer,
$field_type,
$value);
$rows[] = array(
$field_name,
$field_value,
);
}
}
$property_list = new PHUIPropertyListView();
$property_list->setStacked(true);
foreach ($rows as $row) {
$property_list->addProperty($row[0], $row[1]);
}
$box = new PHUIObjectBoxView();
$box->setHeaderText(pht('Object Transcript'));
$box->appendChild($property_list);
return $box;
}
private function buildTransactionsTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$xaction_phids = $this->getTranscriptTransactionPHIDs($xscript);
if ($xaction_phids) {
$object = $xscript->getObject();
$query = PhabricatorApplicationTransactionQuery::newQueryForObject(
$object);
$xactions = $query
->setViewer($viewer)
->withPHIDs($xaction_phids)
->execute();
$xactions = mpull($xactions, null, 'getPHID');
} else {
$xactions = array();
}
$rows = array();
foreach ($xaction_phids as $xaction_phid) {
$xaction = idx($xactions, $xaction_phid);
$xaction_identifier = $xaction_phid;
$xaction_date = null;
$xaction_display = null;
if ($xaction) {
$xaction_identifier = $xaction->getID();
$xaction_date = phabricator_datetime(
$xaction->getDateCreated(),
$viewer);
// Since we don't usually render transactions outside of the context
// of objects, some of them might depend on missing object data. Out of
// an abundance of caution, catch any rendering issues.
try {
$xaction_display = $xaction->getTitle();
} catch (Exception $ex) {
$xaction_display = $ex->getMessage();
}
}
$rows[] = array(
$xaction_identifier,
$xaction_display,
$xaction_date,
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('ID'),
pht('Transaction'),
pht('Date'),
))
->setColumnClasses(
array(
null,
'wide',
null,
));
$box_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Transactions'))
->setTable($table_view);
return $box_view;
}
private function buildProfilerTranscriptPanel(HeraldTranscript $xscript) {
$viewer = $this->getViewer();
$object_xscript = $xscript->getObjectTranscript();
$profile = $object_xscript->getProfile();
// If this is an older transcript without profiler information, don't
// show anything.
if ($profile === null) {
return null;
}
$profile = isort($profile, 'elapsed');
$profile = array_reverse($profile);
$phids = array();
foreach ($profile as $frame) {
if ($frame['type'] === 'rule') {
$phids[] = $frame['key'];
}
}
$handles = $viewer->loadHandles($phids);
$field_map = HeraldField::getAllFields();
$rows = array();
foreach ($profile as $frame) {
$cost = $frame['elapsed'];
$cost = 1000000 * $cost;
$cost = pht('%sus', new PhutilNumber($cost));
$type = $frame['type'];
switch ($type) {
case 'rule':
$type_display = pht('Rule');
break;
case 'field':
$type_display = pht('Field');
break;
default:
$type_display = $type;
break;
}
$key = $frame['key'];
switch ($type) {
case 'field':
$field_object = idx($field_map, $key);
if ($field_object) {
$key_display = $field_object->getHeraldFieldName();
} else {
$key_display = $key;
}
break;
case 'rule':
$key_display = $handles[$key]->renderLink();
break;
default:
$key_display = $key;
break;
}
$rows[] = array(
$type_display,
$key_display,
$cost,
pht('%s', new PhutilNumber($frame['count'])),
);
}
$table_view = id(new AphrontTableView($rows))
->setHeaders(
array(
pht('Type'),
pht('What'),
pht('Cost'),
pht('Count'),
))
->setColumnClasses(
array(
null,
'wide',
'right',
'right',
));
$box_view = id(new PHUIObjectBoxView())
->setHeaderText(pht('Profile'))
->setTable($table_view);
return $box_view;
}
private function getViewKey(AphrontRequest $request) {
$view_key = $request->getURIData('view');
if ($view_key === null) {
return 'rules';
}
switch ($view_key) {
case 'fields':
case 'xactions':
case 'profile':
return $view_key;
default:
return null;
}
}
private function newSideNavView(
HeraldTranscript $xscript,
$view_key) {
$base_uri = urisprintf(
'transcript/%d/',
$xscript->getID());
$base_uri = $this->getApplicationURI($base_uri);
$base_uri = new PhutilURI($base_uri);
$nav = id(new AphrontSideNavFilterView())
->setBaseURI($base_uri);
$nav->newLink('rules')
->setHref($base_uri)
->setName(pht('Rules'))
->setIcon('fa-list-ul');
$nav->newLink('fields')
->setName(pht('Field Values'))
->setIcon('fa-file-text-o');
$xaction_phids = $this->getTranscriptTransactionPHIDs($xscript);
$has_xactions = (bool)$xaction_phids;
$nav->newLink('xactions')
->setName(pht('Transactions'))
->setIcon('fa-forward')
->setDisabled(!$has_xactions);
$nav->newLink('profile')
->setName(pht('Profiler'))
->setIcon('fa-tachometer');
$nav->selectFilter($view_key);
return $nav;
}
private function newContentView(
HeraldTranscript $xscript,
$view_key) {
switch ($view_key) {
case 'rules':
$content = $this->buildActionTranscriptPanel($xscript);
break;
case 'fields':
$content = $this->buildObjectTranscriptPanel($xscript);
break;
case 'xactions':
$content = $this->buildTransactionsTranscriptPanel($xscript);
break;
case 'profile':
$content = $this->buildProfilerTranscriptPanel($xscript);
break;
default:
throw new Exception(pht('Unknown view key "%s".', $view_key));
}
return $content;
}
private function getTranscriptTransactionPHIDs(HeraldTranscript $xscript) {
$object_xscript = $xscript->getObjectTranscript();
$xaction_phids = $object_xscript->getAppliedTransactionPHIDs();
// If the value is "null", this is an older transcript or this adapter
// does not use transactions.
//
// (If the value is "array()", this is a modern transcript which uses
// transactions, there just weren't any applied.)
if ($xaction_phids === null) {
return array();
}
$object = $xscript->getObject();
// If this object doesn't implement the right interface, we won't be
// able to load the transactions.
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
return array();
}
return $xaction_phids;
}
private function newHeaderView(HeraldTranscript $xscript, $title) {
$header = id(new PHUIHeaderView())
->setHeader($title)
->setHeaderIcon('fa-list-ul');
if ($xscript->getDryRun()) {
$dry_run_tag = id(new PHUITagView())
->setType(PHUITagView::TYPE_SHADE)
->setColor(PHUITagView::COLOR_VIOLET)
->setName(pht('Dry Run'))
->setIcon('fa-exclamation-triangle');
$header->addTag($dry_run_tag);
}
return $header;
}
}
Function Calls
None |
Stats
MD5 | 0dc8180f3f9587aa050210c5ff3f6652 |
Eval Count | 0 |
Decode Time | 152 ms |