1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 00:32:42 +01:00

Rough cut of herald transcripts and Differential adapter.

This commit is contained in:
epriestley 2011-03-24 21:32:26 -07:00
parent 0b68b0f5bc
commit 370ba966db
17 changed files with 1042 additions and 38 deletions

View file

@ -267,6 +267,7 @@ return array(
'image/jpeg' => 'image/jpeg',
'image/jpg' => 'image/jpg',
'image/png' => 'image/png',
'image/gif' => 'image/gif',
'text/plain' => 'text/plain; charset=utf-8',
),

View file

@ -188,6 +188,7 @@ phutil_register_library_map(array(
'HeraldController' => 'applications/herald/controller/base',
'HeraldDAO' => 'applications/herald/storage/base',
'HeraldDeleteController' => 'applications/herald/controller/delete',
'HeraldDifferentialRevisionAdapter' => 'applications/herald/adapter/differential',
'HeraldDryRunAdapter' => 'applications/herald/adapter/dryrun',
'HeraldEffect' => 'applications/herald/engine/effect',
'HeraldEngine' => 'applications/herald/engine/engine',
@ -204,6 +205,8 @@ phutil_register_library_map(array(
'HeraldRuleTranscript' => 'applications/herald/storage/transcript/rule',
'HeraldTestConsoleController' => 'applications/herald/controller/test',
'HeraldTranscript' => 'applications/herald/storage/transcript/base',
'HeraldTranscriptController' => 'applications/herald/controller/transcript',
'HeraldTranscriptListController' => 'applications/herald/controller/transcriptlist',
'HeraldValueTypeConfig' => 'applications/herald/config/valuetype',
'Javelin' => 'infrastructure/javelin/api',
'LiskDAO' => 'storage/lisk/dao',
@ -539,6 +542,7 @@ phutil_register_library_map(array(
'HeraldController' => 'PhabricatorController',
'HeraldDAO' => 'PhabricatorLiskDAO',
'HeraldDeleteController' => 'HeraldController',
'HeraldDifferentialRevisionAdapter' => 'HeraldObjectAdapter',
'HeraldDryRunAdapter' => 'HeraldObjectAdapter',
'HeraldHomeController' => 'HeraldController',
'HeraldNewController' => 'HeraldController',
@ -546,6 +550,8 @@ phutil_register_library_map(array(
'HeraldRuleController' => 'HeraldController',
'HeraldTestConsoleController' => 'HeraldController',
'HeraldTranscript' => 'HeraldDAO',
'HeraldTranscriptController' => 'HeraldController',
'HeraldTranscriptListController' => 'HeraldController',
'ManiphestController' => 'PhabricatorController',
'ManiphestDAO' => 'PhabricatorLiskDAO',
'ManiphestTask' => 'ManiphestDAO',

View file

@ -225,7 +225,8 @@ class AphrontDefaultApplicationConfiguration
'delete/(?P<id>\d+)/$' => 'HeraldDeleteController',
'test/$' => 'HeraldTestConsoleController',
'transcript/$' => 'HeraldTranscriptListController',
'transcript/(?P<id>\d+)/$' => 'HeraldTranscriptController',
'transcript/(?P<id>\d+)/(?:(?P<filter>\w+)/)?$'
=> 'HeraldTranscriptController',
),
);

View file

@ -38,6 +38,11 @@ class DifferentialRevisionViewController extends DifferentialController {
$diffs = $revision->loadDiffs();
if (!$diffs) {
throw new Exception(
"This revision has no diffs. Something has gone quite wrong.");
}
$diff_vs = $request->getInt('vs');
$target = end($diffs);

View file

@ -0,0 +1,257 @@
<?php
/*
* Copyright 2011 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 HeraldDifferentialRevisionAdapter extends HeraldObjectAdapter {
protected $revision;
protected $changesets;
protected $diff = null;
protected $explicitCCs;
protected $explicitReviewers;
protected $forbiddenCCs;
protected $forbiddenReviewers;
protected $newCCs = array();
protected $remCCs = array();
public function __construct(DifferentialRevision $revision) {
$revision->loadRelationships();
$this->revision = $revision;
}
public function setDiff(Diff $diff) {
$this->diff = $diff;
return $this;
}
public function setExplicitCCs($explicit_ccs) {
$this->explicitCCs = $explicit_ccs;
return $this;
}
public function setExplicitReviewers($explicit_reviewers) {
$this->explicitReviewers = $explicit_reviewers;
return $this;
}
public function setForbiddenCCs($forbidden_ccs) {
$this->forbiddenCCs = $forbidden_ccs;
return $this;
}
public function setForbiddenReviewers($forbidden_reviewers) {
$this->forbiddenReviewers = $forbidden_reviewers;
return $this;
}
public function getCCsAddedByHerald() {
return array_diff_key($this->newCCs, $this->remCCs);
}
public function getCCsRemovedByHerald() {
return $this->remCCs;
}
public function getPHID() {
return $this->revision->getPHID();
}
public function getHeraldName() {
return $this->revision->getTitle();
}
public function getHeraldTypeName() {
return HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
}
protected function loadChangesets() {
if ($this->changesets) {
return $this->changesets;
}
$diff = $this->loadDiff();
$changes = $diff->getChangesets();
return ($this->changesets = $changes);
}
protected function loadDiff() {
if ($this->diff === null) {
$this->diff = $this->revision->getActiveDiff();
}
return $this->diff;
}
protected function getContentDictionary() {
$changes = $this->loadChangesets();
$hunks = array();
if ($changes) {
$hunks = id(new DifferentialHunk())->loadAllwhere(
'changesetID in (%Ld)',
mpull($changes, 'getID'));
}
$dict = array();
$hunks = mgroup($hunks, 'getChangesetID');
$changes = mpull($changes, null, 'getID');
foreach ($changes as $id => $change) {
$filename = $change->getFilename();
$content = array();
foreach (idx($hunks, $id, array()) as $hunk) {
$content[] = $hunk->makeChanges();
}
$dict[$filename] = implode("\n", $content);
}
return $dict;
}
public function getHeraldField($field) {
switch ($field) {
case HeraldFieldConfig::FIELD_TITLE:
return $this->revision->getTitle();
break;
case HeraldFieldConfig::FIELD_BODY:
return $this->revision->getSummary()."\n".
$this->revision->getTestPlan();
break;
case HeraldFieldConfig::FIELD_AUTHOR:
return $this->revision->getAuthorPHID();
break;
case HeraldFieldConfig::FIELD_DIFF_FILE:
$changes = $this->loadChangesets();
return array_values(mpull($changes, 'getFilename'));
case HeraldFieldConfig::FIELD_CC:
if (isset($this->explicitCCs)) {
return array_keys($this->explicitCCs);
} else {
return $this->revision->getCCPHIDs();
}
case HeraldFieldConfig::FIELD_REVIEWERS:
if (isset($this->explicitReviewers)) {
return array_keys($this->explicitReviewers);
} else {
return $this->revision->getReviewers();
}
/* TODO
case HeraldFieldConfig::FIELD_REPOSITORY:
$id = $this->revision->getRepositoryID();
if (!$id) {
return null;
}
require_module_lazy('intern/repository');
$repository = RepositoryRef::getByID($id);
if (!$repository) {
return null;
}
return $repository->getFBID();
*/
case HeraldFieldConfig::FIELD_DIFF_CONTENT:
return $this->getContentDictionary();
/* TODO
case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE:
return mpull(
DiffOwners::getPackages($this->loadDiff()),
'getFBID');
*/
/* TODO
case HeraldFieldConfig::FIELD_AFFECTED_PACKAGE_OWNER:
return DiffOwners::getOwners($this->loadDiff());
*/
default:
throw new Exception("Invalid field '{$field}'.");
}
}
public function applyHeraldEffects(array $effects) {
$result = array();
if ($this->explicitCCs) {
$effect = new HeraldEffect();
$effect->setAction(HeraldActionConfig::ACTION_ADD_CC);
$effect->setTarget(array_keys($this->explicitCCs));
$effect->setReason(
'CCs provided explicitly by revision author or carried over from a '.
'previous version of the revision.');
$result[] = new HeraldApplyTranscript(
$effect,
true,
'Added addresses to CC list.');
}
$forbidden_ccs = array_fill_keys(
nonempty($this->forbiddenCCs, array()),
true);
foreach ($effects as $effect) {
$action = $effect->getAction();
switch ($action) {
case HeraldActionConfig::ACTION_NOTHING:
$result[] = new HeraldApplyTranscript(
$effect,
true,
'OK, did nothing.');
break;
case HeraldActionConfig::ACTION_ADD_CC:
$base_target = $effect->getTarget();
$forbidden = array();
foreach ($base_target as $key => $fbid) {
if (isset($forbidden_ccs[$fbid])) {
$forbidden[] = $fbid;
unset($base_target[$key]);
} else {
$this->newCCs[$fbid] = true;
}
}
if ($forbidden) {
$failed = clone $effect;
$failed->setTarget($forbidden);
if ($base_target) {
$effect->setTarget($base_target);
$result[] = new HeraldApplyTranscript(
$effect,
true,
'Added these addresses to CC list. Others could not be added.');
}
$result[] = new HeraldApplyTranscript(
$failed,
false,
'CC forbidden, these addresses have unsubscribed.');
} else {
$result[] = new HeraldApplyTranscript(
$effect,
true,
'Added addresses to CC list.');
}
break;
case HeraldActionConfig::ACTION_REMOVE_CC:
foreach ($effect->getTarget() as $fbid) {
$this->remCCs[$fbid] = true;
}
$result[] = new HeraldApplyTranscript(
$effect,
true,
'Removed addresses from CC list.');
break;
default:
throw new Exception("No rules to handle action '{$action}'.");
}
}
return $result;
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/differential/storage/hunk');
phutil_require_module('phabricator', 'applications/herald/adapter/base');
phutil_require_module('phabricator', 'applications/herald/config/action');
phutil_require_module('phabricator', 'applications/herald/config/contenttype');
phutil_require_module('phabricator', 'applications/herald/config/field');
phutil_require_module('phabricator', 'applications/herald/engine/effect');
phutil_require_module('phabricator', 'applications/herald/storage/transcript/apply');
phutil_require_module('phutil', 'utils');
phutil_require_source('HeraldDifferentialRevisionAdapter.php');

View file

@ -444,7 +444,7 @@ class HeraldRuleController extends HeraldController {
return array(
'source' => array(
'email' => '/typeahead/common/mailable/',
'user' => '/typeahead/common/user/',
'user' => '/typeahead/common/users/',
'repository' => '/typeahead/common/repository/',
/*
'tag' => '/datasource/tag/',

View file

@ -8,6 +8,7 @@
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
phutil_require_module('phabricator', 'applications/herald/adapter/differential');
phutil_require_module('phabricator', 'applications/herald/adapter/dryrun');
phutil_require_module('phabricator', 'applications/herald/controller/base');
phutil_require_module('phabricator', 'applications/herald/engine/engine');

View file

@ -0,0 +1,543 @@
<?php
/*
* Copyright 2011 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 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!');
}
$field_names = HeraldFieldConfig::getFieldMap();
$condition_names = HeraldConditionConfig::getConditionMap();
$action_names = HeraldActionConfig::getActionMap();
require_celerity_resource('herald-test-css');
$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;
$object_xscript = $xscript->getObjectTranscript();
$nav = $this->buildSideNav();
$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);
/*
$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>;
}
if (!$object_xscript) {
$notice =
<x:frag>
<tools:notice title="Old Transcript">
Details of this transcript have been discarded. Full transcripts
are retained for 30 days.
</tools:notice>
{$notice}
</x:frag>;
}
return
<herald:standard-page title="Transcript">
<div style="padding: 1em;">
<tools:side-nav items={$this->renderNavItems()}>
{$notice}
{$apply_xscript_markup}
{$rule_table}
{$object_xscript_table}
</tools:side-nav>
</div>
</herald:standard-page>;
*/
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 (!$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 = '<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();
$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 =
'<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) {
$data += array(
'Object Name' => $object_xscript->getName(),
'Object Type' => $object_xscript->getType(),
'Object PHID' => $object_xscript->getPHID(),
);
}
$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 {
$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;
}
}

View file

@ -0,0 +1,24 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/herald/config/action');
phutil_require_module('phabricator', 'applications/herald/config/condition');
phutil_require_module('phabricator', 'applications/herald/config/field');
phutil_require_module('phabricator', 'applications/herald/controller/base');
phutil_require_module('phabricator', 'applications/herald/storage/transcript/base');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'infrastructure/celerity/api');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/layout/sidenav');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('HeraldTranscriptController.php');

View file

@ -0,0 +1,120 @@
<?php
/*
* Copyright 2011 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 HeraldTranscriptListController extends HeraldController {
public function processRequest() {
$request = $this->getRequest();
// Pull these objects manually since the serialized fields are gigantic.
$transcript = new HeraldTranscript();
$data = queryfx_all(
$transcript->establishConnection('r'),
'SELECT id, objectPHID, time, duration, dryRun FROM %T
ORDER BY id DESC
LIMIT 100',
$transcript->getTableName());
/*
$conn_r = smc_get_db('cdb.herald', 'r');
$page_size = 100;
$pager = new SimplePager();
$pager->setPageSize($page_size);
$pager->setOffset((((int)$request->getInt('page')) - 1) * $page_size);
$pager->order('id', array('id'));
$fbid = $request->getInt('fbid');
if ($fbid) {
$filter = qsprintf(
$conn_r,
'WHERE objectID = %d',
$fbid);
} else {
$filter = '';
}
$data = $pager->select(
$conn_r,
'id, objectID, time, duration, dryRun FROM transcript %Q',
$filter);
*/
$handles = array();
if ($data) {
$phids = ipull($data, 'objectPHID', 'objectPHID');
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
}
$rows = array();
foreach ($data as $xscript) {
$rows[] = array(
date('F jS', $xscript['time']),
date('g:i:s A', $xscript['time']),
$handles[$xscript['objectPHID']]->renderLink(),
$xscript['dryRun'] ? 'Yes' : '',
number_format((int)(1000 * $xscript['duration'])).' ms',
phutil_render_tag(
'a',
array(
'href' => '/herald/transcript/'.$xscript['id'].'/',
'class' => 'button small grey',
),
'View Transcript'),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Date',
'Time',
'Object',
'Dry Run',
'Duration',
'View',
));
$table->setColumnClasses(
array(
'',
'right',
'wide wrap',
'',
'',
'action',
));
$panel = new AphrontPanelView();
$panel->setHeader('Herald Transcripts');
$panel->appendChild($table);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Herald Transcripts',
'tab' => 'transcripts',
));
}
}

View file

@ -0,0 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/herald/controller/base');
phutil_require_module('phabricator', 'applications/herald/storage/transcript/base');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('HeraldTranscriptListController.php');

View file

@ -18,7 +18,7 @@
class HeraldEffect {
protected $objectID;
protected $objectPHID;
protected $action;
protected $target;
@ -27,13 +27,13 @@ class HeraldEffect {
protected $reason;
public function setObjectID($object_id) {
$this->objectID = $object_id;
public function setObjectPHID($object_phid) {
$this->objectPHID = $object_phid;
return $this;
}
public function getObjectID() {
return $this->objectID;
public function getObjectPHID() {
return $this->objectPHID;
}
public function setAction($action) {

View file

@ -26,7 +26,7 @@ class HeraldEngine {
protected $fieldCache = array();
protected $object = null;
public static function loadAndApplyRules(IHeraldable $object) {
public static function loadAndApplyRules(HeraldObjectAdapter $object) {
$content_type = $object->getHeraldTypeName();
$rules = HeraldRule::loadAllByContentTypeWithFullData($content_type);
@ -37,13 +37,13 @@ class HeraldEngine {
return $engine->getTranscript();
}
public function applyRules(array $rules, IHeraldable $object) {
public function applyRules(array $rules, HeraldObjectAdapter $object) {
$t_start = microtime(true);
$rules = mpull($rules, null, 'getID');
$this->transcript = new HeraldTranscript();
$this->transcript->setObjectID((string)$object->getFBID());
$this->transcript->setObjectPHID((string)$object->getPHID());
$this->fieldCache = array();
$this->results = array();
$this->rules = $rules;
@ -71,7 +71,7 @@ class HeraldEngine {
"Don't do this! You have formed an unresolvable cycle in the ".
"dependency graph!");
$xscript->setRuleName($rules[$rule_id]->getName());
$xscript->setRuleOwner($rules[$rule_id]->getOwnerID());
$xscript->setRuleOwner($rules[$rule_id]->getAuthorPHID());
$this->transcript->addRuleTranscript($xscript);
}
$rule_matches = false;
@ -86,7 +86,7 @@ class HeraldEngine {
}
$object_transcript = new HeraldObjectTranscript();
$object_transcript->setFBID($object->getFBID());
$object_transcript->setPHID($object->getPHID());
$object_transcript->setName($object->getHeraldName());
$object_transcript->setType($object->getHeraldTypeName());
$object_transcript->setFields($this->fieldCache);
@ -100,7 +100,7 @@ class HeraldEngine {
return $effects;
}
public function applyEffects(array $effects, IHeraldable $object) {
public function applyEffects(array $effects, HeraldObjectAdapter $object) {
if ($object instanceof DryRunHeraldable) {
$this->transcript->setDryRun(true);
} else {
@ -121,7 +121,10 @@ class HeraldEngine {
return $this->transcript;
}
protected function doesRuleMatch(HeraldRule $rule, IHeraldable $object) {
protected function doesRuleMatch(
HeraldRule $rule,
HeraldObjectAdapter $object) {
$id = $rule->getID();
if (isset($this->results[$id])) {
@ -157,7 +160,7 @@ class HeraldEngine {
$reason = "Rule failed automatically because it has no conditions.";
$result = false;
/* TOOD: Restore this in some form?
} else if (!is_fb_employee($rule->getOwnerID())) {
} else if (!is_fb_employee($rule->getAuthorPHID())) {
$reason = "Rule failed automatically because its owner is not an ".
"active employee.";
$result = false;
@ -195,7 +198,7 @@ class HeraldEngine {
$rule_transcript->setResult($result);
$rule_transcript->setReason($reason);
$rule_transcript->setRuleName($rule->getName());
$rule_transcript->setRuleOwner($rule->getOwnerID());
$rule_transcript->setRuleOwner($rule->getAuthorPHID());
$this->transcript->addRuleTranscript($rule_transcript);
@ -205,12 +208,12 @@ class HeraldEngine {
protected function doesConditionMatch(
HeraldRule $rule,
HeraldCondition $condition,
IHeraldable $object) {
HeraldObjectAdapter $object) {
$object_value = $this->getConditionObjectValue($condition, $object);
$test_value = $condition->getValue();
$cond = $condition->getCondition();
$cond = $condition->getFieldCondition();
$transcript = new HeraldConditionTranscript();
$transcript->setRuleID($rule->getID());
@ -241,10 +244,10 @@ class HeraldEngine {
$result = ($object_value != $test_value);
break;
case HeraldConditionConfig::CONDITION_IS_ME:
$result = ($object_value == $rule->getOwnerID());
$result = ($object_value == $rule->getAuthorPHID());
break;
case HeraldConditionConfig::CONDITION_IS_NOT_ME:
$result = ($object_value != $rule->getOwnerID());
$result = ($object_value != $rule->getAuthorPHID());
break;
case HeraldConditionConfig::CONDITION_IS_ANY:
$test_value = array_flip($test_value);
@ -364,7 +367,7 @@ class HeraldEngine {
protected function getConditionObjectValue(
HeraldCondition $condition,
IHeraldable $object) {
HeraldObjectAdapter $object) {
$field = $condition->getFieldName();
@ -391,7 +394,7 @@ class HeraldEngine {
case HeraldFieldConfig::FIELD_AUTHOR:
case HeraldFieldConfig::FIELD_REPOSITORY:
case HeraldFieldConfig::FIELD_MERGE_REQUESTER:
// TODO: Type should be fbid.
// TODO: Type should be PHID.
$result = $this->object->getHeraldField($field);
break;
case HeraldFieldConfig::FIELD_TAGS:
@ -424,11 +427,14 @@ class HeraldEngine {
return $result;
}
protected function getRuleEffects(HeraldRule $rule, IHeraldable $object) {
protected function getRuleEffects(
HeraldRule $rule,
HeraldObjectAdapter $object) {
$effects = array();
foreach ($rule->getActions() as $action) {
$effect = new HeraldEffect();
$effect->setObjectID($object->getFBID());
$effect->setObjectPHID($object->getPHID());
$effect->setAction($action->getAction());
$effect->setTarget($action->getTarget());

View file

@ -19,7 +19,7 @@
class HeraldTranscript extends HeraldDAO {
protected $id;
protected $fbid;
protected $phid;
protected $objectTranscript;
protected $ruleTranscripts = array();
@ -28,10 +28,9 @@ class HeraldTranscript extends HeraldDAO {
protected $time;
protected $host;
protected $path;
protected $duration;
protected $objectID;
protected $objectPHID;
protected $dryRun;
public function getXHeraldRulesHeader() {
@ -61,7 +60,8 @@ class HeraldTranscript extends HeraldDAO {
protected function getConfiguration() {
// Ugh. Too much of a mess to deal with.
return array(
self::CONFIG_AUX_FBID => 'HERALD_TRANSCRIPT',
self::CONFIG_AUX_PHID => true,
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'objectTranscript' => self::SERIALIZATION_PHP,
'ruleTranscripts' => self::SERIALIZATION_PHP,
@ -74,7 +74,6 @@ class HeraldTranscript extends HeraldDAO {
public function __construct() {
$this->time = time();
$this->host = php_uname('n');
$this->path = realpath($_SERVER['PHP_ROOT']);
}
public function addApplyTranscript(HeraldApplyTranscript $transcript) {
@ -131,14 +130,14 @@ class HeraldTranscript extends HeraldDAO {
public function getMetadataMap() {
return array(
'Run At Epoch' => date('F jS, g:i A', $this->time),
'Run On Host' => $this->host.':'.$this->path,
'Run At Epoch' => date('F jS, g:i:s A', $this->time),
'Run On Host' => $this->host,
'Run Duration' => (int)(1000 * $this->duration).' ms',
);
}
public function getURI() {
return 'http://tools.facebook.com/herald/transcript/'.$this->getID().'/';
public function generatePHID() {
return PhabricatorPHID::generateNewPHID('HLXS');
}
}

View file

@ -7,6 +7,7 @@
phutil_require_module('phabricator', 'applications/herald/storage/base');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phutil', 'utils');

View file

@ -18,18 +18,18 @@
class HeraldObjectTranscript {
protected $fbid;
protected $phid;
protected $type;
protected $name;
protected $fields;
public function setFBID($fbid) {
$this->fbid = $fbid;
public function setPHID($phid) {
$this->phid = $phid;
return $this;
}
public function getFBID() {
return $this->fbid;
public function getPHID() {
return $this->phid;
}
public function setType($type) {