/src/applications/herald/controller/transcript/HeraldTranscriptController.php
PHP | 535 lines | 376 code | 80 blank | 79 comment | 53 complexity | a9f66a7e7fbbe6a7c48ef3d8f0ad8623 MD5 | raw file
Possible License(s): JSON, MPL-2.0-no-copyleft-exception, Apache-2.0, BSD-3-Clause, LGPL-2.0, MIT, LGPL-2.1, LGPL-3.0
- <?php
- /*
- * Copyright 2012 Facebook, Inc.
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
- class HeraldTranscriptController extends HeraldController {
- const FILTER_AFFECTED = 'affected';
- const FILTER_OWNED = 'owned';
- const FILTER_ALL = 'all';
- private $id;
- private $filter;
- private $handles;
- public function getFilter() {
- return 'transcript';
- }
- public function willProcessRequest(array $data) {
- $this->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(
- '<p>Details of this transcript have been garbage collected.</p>');
- $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 =
- <tools:notice title="Dry Run">
- This was a dry run to test Herald rules, no actions were executed.
- </tools:notice>;
- }
- */
- 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
- '<span class="condition-test-value">'.
- phutil_escape_html($value).
- '</span>';
- }
- 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::getActionMessageMapForRuleType(
- HeraldRuleTypeConfig::RULE_TYPE_GLOBAL);
- $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 = '<empty>';
- }
- } 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 = '<span class="outcome-success">SUCCESS</span>';
- } else {
- $outcome = '<span class="outcome-failure">FAILURE</span>';
- }
- $outcome .= ' '.phutil_escape_html($apply_xscript->getAppliedReason());
- $rows[] = array(
- phutil_escape_html($action_names[$apply_xscript->getAction()]),
- $target,
- '<strong>Taken because:</strong> '.
- phutil_escape_html($apply_xscript->getReason()).
- '<br />'.
- '<strong>Outcome:</strong> '.$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();
- $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 =
- '<div class="herald-condition-note">'.
- phutil_escape_html($cond->getNote()).
- '</div>';
- } else {
- $note = null;
- }
- if ($cond->getResult()) {
- $result =
- '<span class="herald-outcome condition-pass">'.
- "\xE2\x9C\x93".
- '</span>';
- } else {
- $result =
- '<span class="herald-outcome condition-fail">'.
- "\xE2\x9C\x98".
- '</span>';
- }
- $cond_markup[] =
- '<li>'.
- $result.' Condition: '.
- phutil_escape_html($field_names[$cond->getFieldName()]).
- ' '.
- phutil_escape_html($condition_names[$cond->getCondition()]).
- ' '.
- $this->renderConditionTestValue($cond, $handles).
- $note.
- '</li>';
- }
- if ($rule->getResult()) {
- $result = '<span class="herald-outcome rule-pass">PASS</span>';
- $class = 'herald-rule-pass';
- } else {
- $result = '<span class="herald-outcome rule-fail">FAIL</span>';
- $class = 'herald-rule-fail';
- }
- $cond_markup[] =
- '<li>'.$result.' '.phutil_escape_html($rule->getReason()).'</li>';
- /*
- if ($rule->getResult()) {
- $actions = idx($action_xscript, $rule_id, array());
- if ($actions) {
- $cond_markup[] = <li><div class="action-header">Actions</div></li>;
- foreach ($actions as $action) {
- $target = $action->getTarget();
- if ($target) {
- foreach ((array)$target as $k => $phid) {
- $target[$k] = $handles[$phid]->getName();
- }
- $target = <strong>: {implode(', ', $target)}</strong>;
- }
- $cond_markup[] =
- <li>
- {$action_names[$action->getAction()]}
- {$target}
- </li>;
- }
- }
- }
- */
- $user_phid = $this->getRequest()->getUser()->getPHID();
- $name = $rule->getRuleName();
- if ($rule->getRuleOwner() == $user_phid) {
- // $name = <a href={"/herald/rule/".$rule->getRuleID()."/"}>{$name}</a>;
- }
- $rule_markup[] =
- phutil_render_tag(
- 'li',
- array(
- 'class' => $class,
- ),
- '<div class="rule-name">'.
- '<strong>'.phutil_escape_html($name).'</strong> '.
- phutil_escape_html($handles[$rule->getRuleOwner()]->getName()).
- '</div>'.
- '<ul>'.implode("\n", $cond_markup).'</ul>');
- }
- $panel = new AphrontPanelView();
- $panel->setHeader('Rule Details');
- $panel->appendChild(
- '<ul class="herald-explain-list">'.
- implode("\n", $rule_markup).
- '</ul>');
- 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;
- }
- }