id = $data['id'];
$map = $this->getFilterMap();
$this->filter = idx($data, 'filter');
if (empty($map[$this->filter])) {
$this->filter = self::FILTER_AFFECTED;
}
}
public function processRequest() {
$xscript = id(new HeraldTranscript())->load($this->id);
if (!$xscript) {
throw new Exception('Uknown transcript!');
}
require_celerity_resource('herald-test-css');
$nav = $this->buildSideNav();
$object_xscript = $xscript->getObjectTranscript();
if (!$object_xscript) {
$notice = id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle('Old Transcript')
->appendChild(
'
Details of this transcript have been garbage collected.
');
$nav->appendChild($notice);
} else {
$filter = $this->getFilterPHIDs();
$this->filterTranscript($xscript, $filter);
$phids = array_merge($filter, $this->getTranscriptPHIDs($xscript));
$phids = array_unique($phids);
$phids = array_filter($phids);
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$this->handles = $handles;
$apply_xscript_panel = $this->buildApplyTranscriptPanel(
$xscript);
$nav->appendChild($apply_xscript_panel);
$action_xscript_panel = $this->buildActionTranscriptPanel(
$xscript);
$nav->appendChild($action_xscript_panel);
$object_xscript_panel = $this->buildObjectTranscriptPanel(
$xscript);
$nav->appendChild($object_xscript_panel);
}
/*
TODO
$notice = null;
if ($xscript->getDryRun()) {
$notice =
This was a dry run to test Herald rules, no actions were executed.
;
}
*/
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Transcript',
));
}
protected function renderConditionTestValue($condition, $handles) {
$value = $condition->getTestValue();
if (!is_scalar($value) && $value !== null) {
foreach ($value as $key => $phid) {
$handle = idx($handles, $phid);
if ($handle) {
$value[$key] = $handle->getName();
} else {
// This shouldn't ever really happen as we are supposed to have
// grabbed handles for everything, but be super liberal in what
// we accept here since we expect all sorts of weird issues as we
// version the system.
$value[$key] = 'Unknown Object #'.$phid;
}
}
sort($value);
$value = implode(', ', $value);
}
return
''.
phutil_escape_html($value).
'';
}
private function buildSideNav() {
$nav = new AphrontSideNavView();
$items = array();
$filters = $this->getFilterMap();
foreach ($filters as $key => $name) {
$nav->addNavItem(
phutil_render_tag(
'a',
array(
'href' => '/herald/transcript/'.$this->id.'/'.$key.'/',
'class' =>
($key == $this->filter)
? 'aphront-side-nav-selected'
: null,
),
phutil_escape_html($name)));
}
return $nav;
}
protected function getFilterMap() {
return array(
self::FILTER_AFFECTED => 'Rules that Affected Me',
self::FILTER_OWNED => 'Rules I Own',
self::FILTER_ALL => 'All Rules',
);
}
protected function getFilterPHIDs() {
return array($this->getRequest()->getUser()->getPHID());
/* TODO
$viewer_id = $this->getRequest()->getUser()->getPHID();
$fbids = array();
if ($this->filter == self::FILTER_AFFECTED) {
$fbids[] = $viewer_id;
require_module_lazy('intern/subscriptions');
$datastore = new SubscriberDatabaseStore();
$lists = $datastore->getUserMailmanLists($viewer_id);
foreach ($lists as $list) {
$fbids[] = $list;
}
}
return $fbids;
*/
}
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) {
$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;
}
}
}
}
return $phids;
}
protected function filterTranscript($xscript, $filter_phids) {
$filter_owned = ($this->filter == self::FILTER_OWNED);
$filter_affected = ($this->filter == self::FILTER_AFFECTED);
if (!$filter_owned && !$filter_affected) {
// No filtering to be done.
return;
}
if (!$xscript->getObjectTranscript()) {
return;
}
$user_phid = $this->getRequest()->getUser()->getPHID();
$keep_apply_xscripts = array();
$keep_rule_xscripts = array();
$filter_phids = array_fill_keys($filter_phids, true);
$rule_xscripts = $xscript->getRuleTranscripts();
foreach ($xscript->getApplyTranscripts() as $id => $apply_xscript) {
$rule_id = $apply_xscript->getRuleID();
if ($filter_owned) {
if (empty($rule_xscripts[$rule_id])) {
// No associated rule so you can't own this effect.
continue;
}
if ($rule_xscripts[$rule_id]->getRuleOwner() != $user_phid) {
continue;
}
} else if ($filter_affected) {
$targets = (array)$apply_xscript->getTarget();
if (!array_select_keys($filter_phids, $targets)) {
continue;
}
}
$keep_apply_xscripts[$id] = true;
if ($rule_id) {
$keep_rule_xscripts[$rule_id] = true;
}
}
foreach ($rule_xscripts as $rule_id => $rule_xscript) {
if ($filter_owned && $rule_xscript->getRuleOwner() == $user_phid) {
$keep_rule_xscripts[$rule_id] = true;
}
}
$xscript->setRuleTranscripts(
array_intersect_key(
$xscript->getRuleTranscripts(),
$keep_rule_xscripts));
$xscript->setApplyTranscripts(
array_intersect_key(
$xscript->getApplyTranscripts(),
$keep_apply_xscripts));
$xscript->setConditionTranscripts(
array_intersect_key(
$xscript->getConditionTranscripts(),
$keep_rule_xscripts));
}
private function buildApplyTranscriptPanel($xscript) {
$handles = $this->handles;
$action_names = HeraldActionConfig::getActionMap();
$rows = array();
foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
// TODO: Hacks, this is an approximate guess at the target type.
$target = (array)$apply_xscript->getTarget();
if (!$target) {
if ($apply_xscript->getAction() == HeraldActionConfig::ACTION_NOTHING) {
$target = '';
} else {
$target = '';
}
} else {
foreach ($target as $k => $phid) {
$target[$k] = $handles[$phid]->getName();
}
$target = implode("\n", $target);
}
$target = phutil_escape_html($target);
if ($apply_xscript->getApplied()) {
$outcome = 'SUCCESS';
} else {
$outcome = 'FAILURE';
}
$outcome .= ' '.phutil_escape_html($apply_xscript->getAppliedReason());
$rows[] = array(
phutil_escape_html($action_names[$apply_xscript->getAction()]),
$target,
'Taken because: '.
phutil_escape_html($apply_xscript->getReason()).
'
'.
'Outcome: '.$outcome,
);
}
$table = new AphrontTableView($rows);
$table->setNoDataString('No actions were taken.');
$table->setHeaders(
array(
'Action',
'Target',
'Details',
));
$table->setColumnClasses(
array(
'',
'',
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader('Actions Taken');
$panel->appendChild($table);
return $panel;
}
private function buildActionTranscriptPanel($xscript) {
$action_xscript = mgroup($xscript->getApplyTranscripts(), 'getRuleID');
$field_names = HeraldFieldConfig::getFieldMap();
$condition_names = HeraldConditionConfig::getConditionMap();
$action_names = HeraldActionConfig::getActionMap();
$handles = $this->handles;
$rule_markup = array();
foreach ($xscript->getRuleTranscripts() as $rule_id => $rule) {
$cond_markup = array();
foreach ($xscript->getConditionTranscriptsForRule($rule_id) as $cond) {
if ($cond->getNote()) {
$note =
''.
phutil_escape_html($cond->getNote()).
'
';
} else {
$note = null;
}
if ($cond->getResult()) {
$result =
''.
"\xE2\x9C\x93".
'';
} else {
$result =
''.
"\xE2\x9C\x98".
'';
}
$cond_markup[] =
''.
$result.' Condition: '.
phutil_escape_html($field_names[$cond->getFieldName()]).
' '.
phutil_escape_html($condition_names[$cond->getCondition()]).
' '.
$this->renderConditionTestValue($cond, $handles).
$note.
'';
}
if ($rule->getResult()) {
$result = 'PASS';
$class = 'herald-rule-pass';
} else {
$result = 'FAIL';
$class = 'herald-rule-fail';
}
$cond_markup[] =
''.$result.' '.phutil_escape_html($rule->getReason()).'';
/*
if ($rule->getResult()) {
$actions = idx($action_xscript, $rule_id, array());
if ($actions) {
$cond_markup[] = ;
foreach ($actions as $action) {
$target = $action->getTarget();
if ($target) {
foreach ((array)$target as $k => $phid) {
$target[$k] = $handles[$phid]->getName();
}
$target = : {implode(', ', $target)};
}
$cond_markup[] =
{$action_names[$action->getAction()]}
{$target}
;
}
}
}
*/
$user_phid = $this->getRequest()->getUser()->getPHID();
$name = $rule->getRuleName();
if ($rule->getRuleOwner() == $user_phid) {
// $name = getRuleID()."/"}>{$name};
}
$rule_markup[] =
phutil_render_tag(
'li',
array(
'class' => $class,
),
''.
''.phutil_escape_html($name).' '.
phutil_escape_html($handles[$rule->getRuleOwner()]->getName()).
'
'.
''.implode("\n", $cond_markup).'
');
}
$panel = new AphrontPanelView();
$panel->setHeader('Rule Details');
$panel->appendChild(
''.
implode("\n", $rule_markup).
'
');
return $panel;
}
private function buildObjectTranscriptPanel($xscript) {
$field_names = HeraldFieldConfig::getFieldMap();
$object_xscript = $xscript->getObjectTranscript();
$data = array();
if ($object_xscript) {
$phid = $object_xscript->getPHID();
$handles = id(new PhabricatorObjectHandleData(array($phid)))
->loadHandles();
$data += array(
'Object Name' => $object_xscript->getName(),
'Object Type' => $object_xscript->getType(),
'Object PHID' => $phid,
'Object Link' => $handles[$phid]->renderLink(),
);
}
$data += $xscript->getMetadataMap();
if ($object_xscript) {
foreach ($object_xscript->getFields() as $field => $value) {
$field = idx($field_names, $field, '['.$field.'?]');
$data['Field: '.$field] = $value;
}
}
$rows = array();
foreach ($data as $name => $value) {
if (!is_scalar($value) && !is_null($value)) {
$value = implode("\n", $value);
}
if (strlen($value) > 256) {
$value = phutil_render_tag(
'textarea',
array(
'class' => 'herald-field-value-transcript',
),
phutil_escape_html($value));
} else if ($name === 'Object Link') {
// The link cannot be escaped
} else {
$value = phutil_escape_html($value);
}
$rows[] = array(
phutil_escape_html($name),
$value,
);
}
$table = new AphrontTableView($rows);
$table->setColumnClasses(
array(
'header',
'wide',
));
$panel = new AphrontPanelView();
$panel->setHeader('Object Transcript');
$panel->appendChild($table);
return $panel;
}
}