Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/herald/controller/HeraldHomeController.php b/src/applications/herald/controller/HeraldHomeController.php
index b47928f2ab..16df2de2c2 100644
--- a/src/applications/herald/controller/HeraldHomeController.php
+++ b/src/applications/herald/controller/HeraldHomeController.php
@@ -1,156 +1,157 @@
<?php
final class HeraldHomeController extends HeraldController {
private $contentType;
private $ruleType;
public function willProcessRequest(array $data) {
$this->contentType = idx($data, 'content_type');
$this->ruleType = idx($data, 'rule_type');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
if ($request->isFormPost()) {
$phids = $request->getArr('set_phid');
$phid = head($phids);
$uri = $request->getRequestURI();
if ($phid) {
$uri = $uri->alter('phid', nonempty($phid, null));
}
return id(new AphrontRedirectResponse())->setURI($uri);
}
$query = new HeraldRuleQuery();
$content_type_map = HeraldContentTypeConfig::getContentTypeMap();
if (empty($content_type_map[$this->contentType])) {
$this->contentType = head_key($content_type_map);
}
$content_desc = $content_type_map[$this->contentType];
$query->withContentTypes(array($this->contentType));
$is_admin_page = false;
$show_author = false;
$show_rule_type = false;
$can_create = false;
$has_author_filter = false;
$author_filter_phid = null;
switch ($this->ruleType) {
case 'all':
if (!$user->getIsAdmin()) {
return new Aphront400Response();
}
$is_admin_page = true;
$show_rule_type = true;
$show_author = true;
$has_author_filter = true;
$author_filter_phid = $request->getStr('phid');
if ($author_filter_phid) {
$query->withAuthorPHIDs(array($author_filter_phid));
}
$rule_desc = 'All';
break;
case HeraldRuleTypeConfig::RULE_TYPE_GLOBAL:
$query->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_GLOBAL));
$can_create = true;
$rule_desc = 'Global';
break;
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
default:
$this->ruleType = HeraldRuleTypeConfig::RULE_TYPE_PERSONAL;
$query->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_PERSONAL));
$query->withAuthorPHIDs(array($user->getPHID()));
$can_create = true;
$rule_desc = 'Personal';
break;
}
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'offset');
$pager->setOffset($request->getStr('offset'));
$rules = $query->executeWithOffsetPager($pager);
$need_phids = mpull($rules, 'getAuthorPHID');
$handles = $this->loadViewerHandles($need_phids);
$list_view = id(new HeraldRuleListView())
->setRules($rules)
->setShowAuthor($show_author)
->setShowRuleType($show_rule_type)
->setHandles($handles)
->setUser($user);
$panel = new AphrontPanelView();
$panel->appendChild($list_view);
$panel->appendChild($pager);
+ $panel->setNoBackground();
$panel->setHeader("Herald: {$rule_desc} Rules for {$content_desc}");
if ($can_create) {
$panel->addButton(
phutil_render_tag(
'a',
array(
'href' => '/herald/new/'.$this->contentType.'/'.$this->ruleType.'/',
'class' => 'green button',
),
'Create New Herald Rule'));
}
$nav = $this->renderNav();
$nav->selectFilter('view/'.$this->contentType.'/'.$this->ruleType);
if ($has_author_filter) {
$nav->appendChild($this->renderAuthorFilter($author_filter_phid));
}
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Herald',
'admin' => $is_admin_page,
));
}
private function renderAuthorFilter($phid) {
if ($phid) {
$handle = PhabricatorObjectHandleData::loadOneHandle($phid);
$tokens = array(
$phid => $handle->getFullName(),
);
} else {
$tokens = array();
}
$form = id(new AphrontFormView())
->setUser($this->getRequest()->getUser())
->appendChild(
id(new AphrontFormTokenizerControl())
->setName('set_phid')
->setValue($tokens)
->setLimit(1)
->setLabel('Filter Author')
->setDataSource('/typeahead/common/accounts/'))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Apply Filter'));
$filter = new AphrontListFilterView();
$filter->appendChild($form);
return $filter;
}
}
diff --git a/src/applications/herald/controller/HeraldNewController.php b/src/applications/herald/controller/HeraldNewController.php
index c346939ee7..fdedda66e2 100644
--- a/src/applications/herald/controller/HeraldNewController.php
+++ b/src/applications/herald/controller/HeraldNewController.php
@@ -1,88 +1,89 @@
<?php
final class HeraldNewController extends HeraldController {
private $contentType;
private $ruleType;
public function willProcessRequest(array $data) {
$this->contentType = idx($data, 'type');
$this->ruleType = idx($data, 'rule_type');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$content_type_map = HeraldContentTypeConfig::getContentTypeMap();
if (empty($content_type_map[$this->contentType])) {
$this->contentType = head_key($content_type_map);
}
$rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap();
if (empty($rule_type_map[$this->ruleType])) {
$this->ruleType = HeraldRuleTypeConfig::RULE_TYPE_PERSONAL;
}
// Reorder array to put "personal" first.
$rule_type_map = array_select_keys(
$rule_type_map,
array(
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL,
)) + $rule_type_map;
$captions = array(
HeraldRuleTypeConfig::RULE_TYPE_PERSONAL =>
'Personal rules notify you about events. You own them, but they can '.
'only affect you.',
HeraldRuleTypeConfig::RULE_TYPE_GLOBAL =>
'Global rules notify anyone about events. No one owns them, and '.
'anyone can edit them. Usually, Global rules are used to notify '.
'mailing lists.',
);
$radio = id(new AphrontFormRadioButtonControl())
->setLabel('Type')
->setName('rule_type')
->setValue($this->ruleType);
foreach ($rule_type_map as $value => $name) {
$radio->addButton(
$value,
$name,
idx($captions, $value));
}
$form = id(new AphrontFormView())
->setUser($user)
->setAction('/herald/rule/')
->appendChild(
id(new AphrontFormSelectControl())
- ->setLabel('New rule for')
+ ->setLabel(pht('New rule for'))
->setName('content_type')
->setValue($this->contentType)
->setOptions($content_type_map))
->appendChild($radio)
->appendChild(
id(new AphrontFormSubmitControl())
- ->setValue('Create Rule')
+ ->setValue(pht('Create Rule'))
->addCancelButton('/herald/view/'.$this->contentType.'/'));
$panel = new AphrontPanelView();
- $panel->setHeader('Create New Herald Rule');
+ $panel->setHeader(pht('Create New Herald Rule'));
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
$panel->appendChild($form);
+ $panel->setNoBackground();
$nav = $this->renderNav();
$nav->selectFilter('new');
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Create Herald Rule',
));
}
}
diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php
index eddd1fcc66..81b0bad2f3 100644
--- a/src/applications/herald/controller/HeraldRuleController.php
+++ b/src/applications/herald/controller/HeraldRuleController.php
@@ -1,558 +1,559 @@
<?php
final class HeraldRuleController extends HeraldController {
private $id;
private $filter;
public function willProcessRequest(array $data) {
$this->id = (int)idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$content_type_map = HeraldContentTypeConfig::getContentTypeMap();
$rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap();
if ($this->id) {
$rule = id(new HeraldRule())->load($this->id);
if (!$rule) {
return new Aphront404Response();
}
if (!$this->canEditRule($rule, $user)) {
throw new Exception("You don't own this rule and can't edit it.");
}
} else {
$rule = new HeraldRule();
$rule->setAuthorPHID($user->getPHID());
$rule->setMustMatchAll(true);
$content_type = $request->getStr('content_type');
if (!isset($content_type_map[$content_type])) {
$content_type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
}
$rule->setContentType($content_type);
$rule_type = $request->getStr('rule_type');
if (!isset($rule_type_map[$rule_type])) {
$rule_type = HeraldRuleTypeConfig::RULE_TYPE_GLOBAL;
}
$rule->setRuleType($rule_type);
}
$local_version = id(new HeraldRule())->getConfigVersion();
if ($rule->getConfigVersion() > $local_version) {
throw new Exception(
"This rule was created with a newer version of Herald. You can not ".
"view or edit it in this older version. Upgrade your Phabricator ".
"deployment.");
}
// Upgrade rule version to our version, since we might add newly-defined
// conditions, etc.
$rule->setConfigVersion($local_version);
$rule_conditions = $rule->loadConditions();
$rule_actions = $rule->loadActions();
$rule->attachConditions($rule_conditions);
$rule->attachActions($rule_actions);
$e_name = true;
$errors = array();
if ($request->isFormPost() && $request->getStr('save')) {
list($e_name, $errors) = $this->saveRule($rule, $request);
if (!$errors) {
$uri = '/herald/view/'.
$rule->getContentType().'/'.
$rule->getRuleType().'/';
return id(new AphrontRedirectResponse())->setURI($uri);
}
}
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setErrors($errors);
} else {
$error_view = null;
}
$must_match_selector = $this->renderMustMatchSelector($rule);
$repetition_selector = $this->renderRepetitionSelector($rule);
$handles = $this->loadHandlesForRule($rule);
require_celerity_resource('herald-css');
$content_type_name = $content_type_map[$rule->getContentType()];
$rule_type_name = $rule_type_map[$rule->getRuleType()];
$form = id(new AphrontFormView())
->setUser($user)
->setID('herald-rule-edit-form')
->addHiddenInput('content_type', $rule->getContentType())
->addHiddenInput('rule_type', $rule->getRuleType())
->addHiddenInput('save', 1)
->appendChild(
// Build this explicitly (instead of using addHiddenInput())
// so we can add a sigil to it.
javelin_render_tag(
'input',
array(
'type' => 'hidden',
'name' => 'rule',
'sigil' => 'rule',
)))
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Rule Name')
->setName('name')
->setError($e_name)
->setValue($rule->getName()));
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setValue(
"This <strong>${rule_type_name}</strong> rule triggers for " .
"<strong>${content_type_name}</strong>."))
->appendChild(
id(new AphrontFormInsetView())
->setTitle('Conditions')
->setRightButton(javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'create-condition',
'mustcapture' => true
),
'Create New Condition'))
->setDescription(
'When '.$must_match_selector .
' these conditions are met:')
->setContent(javelin_render_tag(
'table',
array(
'sigil' => 'rule-conditions',
'class' => 'herald-condition-table'
),
'')))
->appendChild(
id(new AphrontFormInsetView())
->setTitle('Action')
->setRightButton(javelin_render_tag(
'a',
array(
'href' => '#',
'class' => 'button green',
'sigil' => 'create-action',
'mustcapture' => true,
),
'Create New Action'))
->setDescription('Take these actions '.$repetition_selector.
' this rule matches:')
->setContent(javelin_render_tag(
'table',
array(
'sigil' => 'rule-actions',
'class' => 'herald-action-table',
),
'')))
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Save Rule')
->addCancelButton('/herald/view/'.$rule->getContentType().'/'));
$this->setupEditorBehavior($rule, $handles);
$panel = new AphrontPanelView();
$panel->setHeader(
$rule->getID()
- ? 'Edit Herald Rule'
- : 'Create Herald Rule');
+ ? pht('Edit Herald Rule')
+ : pht('Create Herald Rule'));
$panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$panel->appendChild($form);
+ $panel->setNoBackground();
$nav = $this->renderNav();
$nav->selectFilter(
'view/'.$rule->getContentType().'/'.$rule->getRuleType());
$nav->appendChild(
array(
$error_view,
$panel,
));
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Edit Rule',
));
}
private function canEditRule($rule, $user) {
return
($user->getIsAdmin()) ||
($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_GLOBAL) ||
($rule->getAuthorPHID() == $user->getPHID());
}
private function saveRule($rule, $request) {
$rule->setName($request->getStr('name'));
$rule->setMustMatchAll(($request->getStr('must_match') == 'all'));
$repetition_policy_param = $request->getStr('repetition_policy');
$rule->setRepetitionPolicy(
HeraldRepetitionPolicyConfig::toInt($repetition_policy_param)
);
$e_name = true;
$errors = array();
if (!strlen($rule->getName())) {
$e_name = "Required";
$errors[] = "Rule must have a name.";
}
$data = json_decode($request->getStr('rule'), true);
if (!is_array($data) ||
!$data['conditions'] ||
!$data['actions']) {
throw new Exception("Failed to decode rule data.");
}
$conditions = array();
foreach ($data['conditions'] as $condition) {
if ($condition === null) {
// We manage this as a sparse array on the client, so may receive
// NULL if conditions have been removed.
continue;
}
$obj = new HeraldCondition();
$obj->setFieldName($condition[0]);
$obj->setFieldCondition($condition[1]);
if (is_array($condition[2])) {
$obj->setValue(array_keys($condition[2]));
} else {
$obj->setValue($condition[2]);
}
$cond_type = $obj->getFieldCondition();
if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP) {
if (@preg_match($obj->getValue(), '') === false) {
$errors[] =
'The regular expression "'.$obj->getValue().'" is not valid. '.
'Regular expressions must have enclosing characters (e.g. '.
'"@/path/to/file@", not "/path/to/file") and be syntactically '.
'correct.';
}
}
if ($cond_type == HeraldConditionConfig::CONDITION_REGEXP_PAIR) {
$json = json_decode($obj->getValue(), true);
if (!is_array($json)) {
$errors[] =
'The regular expression pair "'.$obj->getValue().'" is not '.
'valid JSON. Enter a valid JSON array with two elements.';
} else {
if (count($json) != 2) {
$errors[] =
'The regular expression pair "'.$obj->getValue().'" must have '.
'exactly two elements.';
} else {
$key_regexp = array_shift($json);
$val_regexp = array_shift($json);
if (@preg_match($key_regexp, '') === false) {
$errors[] =
'The first regexp, "'.$key_regexp.'" in the regexp pair '.
'is not a valid regexp.';
}
if (@preg_match($val_regexp, '') === false) {
$errors[] =
'The second regexp, "'.$val_regexp.'" in the regexp pair '.
'is not a valid regexp.';
}
}
}
}
$conditions[] = $obj;
}
$actions = array();
foreach ($data['actions'] as $action) {
if ($action === null) {
// Sparse on the client; removals can give us NULLs.
continue;
}
if (!isset($action[1])) {
// Legitimate for any action which doesn't need a target, like
// "Do nothing".
$action[1] = null;
}
$actions[] = HeraldActionConfig::willSaveAction($rule->getRuleType(),
$rule->getAuthorPHID(),
$action);
}
$rule->attachConditions($conditions);
$rule->attachActions($actions);
if (!$errors) {
try {
$edit_action = $rule->getID() ? 'edit' : 'create';
$rule->openTransaction();
$rule->save();
$rule->saveConditions($conditions);
$rule->saveActions($actions);
$rule->logEdit($request->getUser()->getPHID(), $edit_action);
$rule->saveTransaction();
} catch (AphrontQueryDuplicateKeyException $ex) {
$e_name = "Not Unique";
$errors[] = "Rule name is not unique. Choose a unique name.";
}
}
return array($e_name, $errors);
}
private function setupEditorBehavior($rule, $handles) {
$serial_conditions = array(
array('default', 'default', ''),
);
if ($rule->getConditions()) {
$serial_conditions = array();
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (is_array($value)) {
$value_map = array();
foreach ($value as $k => $fbid) {
$value_map[$fbid] = $handles[$fbid]->getName();
}
$value = $value_map;
}
$serial_conditions[] = array(
$condition->getFieldName(),
$condition->getFieldCondition(),
$value,
);
}
}
$serial_actions = array(
array('default', ''),
);
if ($rule->getActions()) {
$serial_actions = array();
foreach ($rule->getActions() as $action) {
switch ($action->getAction()) {
case HeraldActionConfig::ACTION_FLAG:
$current_value = $action->getTarget();
break;
default:
$target_map = array();
foreach ((array)$action->getTarget() as $fbid) {
$target_map[$fbid] = $handles[$fbid]->getName();
}
$current_value = $target_map;
break;
}
$serial_actions[] = array(
$action->getAction(),
$current_value,
);
}
}
$all_rules = $this->loadRulesThisRuleMayDependUpon($rule);
$all_rules = mpull($all_rules, 'getName', 'getID');
asort($all_rules);
$config_info = array();
$config_info['fields']
= HeraldFieldConfig::getFieldMapForContentType($rule->getContentType());
$config_info['conditions'] = HeraldConditionConfig::getConditionMap();
foreach ($config_info['fields'] as $field => $name) {
$config_info['conditionMap'][$field] = array_keys(
HeraldConditionConfig::getConditionMapForField($field));
}
foreach ($config_info['fields'] as $field => $fname) {
foreach ($config_info['conditions'] as $condition => $cname) {
$config_info['values'][$field][$condition] =
HeraldValueTypeConfig::getValueTypeForFieldAndCondition(
$field,
$condition);
}
}
$config_info['actions'] =
HeraldActionConfig::getActionMessageMap($rule->getContentType(),
$rule->getRuleType());
$config_info['rule_type'] = $rule->getRuleType();
foreach ($config_info['actions'] as $action => $name) {
$config_info['targets'][$action] =
HeraldValueTypeConfig::getValueTypeForAction($action,
$rule->getRuleType());
}
Javelin::initBehavior(
'herald-rule-editor',
array(
'root' => 'herald-rule-edit-form',
'conditions' => (object)$serial_conditions,
'actions' => (object)$serial_actions,
'template' => $this->buildTokenizerTemplates() + array(
'rules' => $all_rules,
'colors' => PhabricatorFlagColor::getColorNameMap(),
'defaultColor' => PhabricatorFlagColor::COLOR_BLUE,
),
'author' => array($rule->getAuthorPHID() =>
$handles[$rule->getAuthorPHID()]->getName()),
'info' => $config_info,
));
}
private function loadHandlesForRule($rule) {
$phids = array();
foreach ($rule->getActions() as $action) {
if (!is_array($action->getTarget())) {
continue;
}
foreach ($action->getTarget() as $target) {
$target = (array)$target;
foreach ($target as $phid) {
$phids[] = $phid;
}
}
}
foreach ($rule->getConditions() as $condition) {
$value = $condition->getValue();
if (is_array($value)) {
foreach ($value as $phid) {
$phids[] = $phid;
}
}
}
$phids[] = $rule->getAuthorPHID();
return $this->loadViewerHandles($phids);
}
/**
* Render the selector for the "When (all of | any of) these conditions are
* met:" element.
*/
private function renderMustMatchSelector($rule) {
return AphrontFormSelectControl::renderSelectTag(
$rule->getMustMatchAll() ? 'all' : 'any',
array(
'all' => 'all of',
'any' => 'any of',
),
array(
'name' => 'must_match',
));
}
/**
* Render the selector for "Take these actions (every time | only the first
* time) this rule matches..." element.
*/
private function renderRepetitionSelector($rule) {
// Make the selector for choosing how often this rule should be repeated
$repetition_policy = HeraldRepetitionPolicyConfig::toString(
$rule->getRepetitionPolicy());
$repetition_options = HeraldRepetitionPolicyConfig::getMapForContentType(
$rule->getContentType());
if (empty($repetition_options)) {
// default option is 'every time'
$repetition_selector = idx(
HeraldRepetitionPolicyConfig::getMap(),
HeraldRepetitionPolicyConfig::EVERY);
return $repetition_selector;
} else if (count($repetition_options) == 1) {
// if there's only 1 option, just pick it for the user
$repetition_selector = reset($repetition_options);
return $repetition_selector;
} else {
return AphrontFormSelectControl::renderSelectTag(
$repetition_policy,
$repetition_options,
array(
'name' => 'repetition_policy',
));
}
}
protected function buildTokenizerTemplates() {
$template = new AphrontTokenizerTemplateView();
$template = $template->render();
return array(
'source' => array(
'email' => '/typeahead/common/mailable/',
'user' => '/typeahead/common/users/',
'repository' => '/typeahead/common/repositories/',
'package' => '/typeahead/common/packages/',
'project' => '/typeahead/common/projects/',
),
'markup' => $template,
);
}
/**
* Load rules for the "Another Herald rule..." condition dropdown, which
* allows one rule to depend upon the success or failure of another rule.
*/
private function loadRulesThisRuleMayDependUpon(HeraldRule $rule) {
// Any rule can depend on a global rule.
$all_rules = id(new HeraldRuleQuery())
->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_GLOBAL))
->withContentTypes(array($rule->getContentType()))
->execute();
if ($rule->getRuleType() == HeraldRuleTypeConfig::RULE_TYPE_PERSONAL) {
// Personal rules may depend upon your other personal rules.
$all_rules += id(new HeraldRuleQuery())
->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_PERSONAL))
->withContentTypes(array($rule->getContentType()))
->withAuthorPHIDs(array($rule->getAuthorPHID()))
->execute();
}
// A rule can not depend upon itself.
unset($all_rules[$rule->getID()]);
return $all_rules;
}
}
diff --git a/src/applications/herald/controller/HeraldRuleEditHistoryController.php b/src/applications/herald/controller/HeraldRuleEditHistoryController.php
index a4e4e33207..5bfd69c2d1 100644
--- a/src/applications/herald/controller/HeraldRuleEditHistoryController.php
+++ b/src/applications/herald/controller/HeraldRuleEditHistoryController.php
@@ -1,48 +1,49 @@
<?php
final class HeraldRuleEditHistoryController extends HeraldController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
}
public function processRequest() {
$request = $this->getRequest();
$edit_query = new HeraldEditLogQuery();
if ($this->id) {
$edit_query->withRuleIDs(array($this->id));
}
$pager = new AphrontPagerView();
$pager->setURI($request->getRequestURI(), 'offset');
$pager->setOffset($request->getStr('offset'));
$edits = $edit_query->executeWithOffsetPager($pager);
$need_phids = mpull($edits, 'getEditorPHID');
$handles = $this->loadViewerHandles($need_phids);
$list_view = id(new HeraldRuleEditHistoryView())
->setEdits($edits)
->setHandles($handles)
->setUser($this->getRequest()->getUser());
$panel = new AphrontPanelView();
- $panel->setHeader('Edit History');
+ $panel->setHeader(pht('Edit History'));
$panel->appendChild($list_view);
+ $panel->setNoBackground();
$nav = $this->renderNav();
$nav->selectFilter('history');
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Rule Edit History',
));
}
}
diff --git a/src/applications/herald/controller/HeraldTestConsoleController.php b/src/applications/herald/controller/HeraldTestConsoleController.php
index f94b165c76..43e517d0c4 100644
--- a/src/applications/herald/controller/HeraldTestConsoleController.php
+++ b/src/applications/herald/controller/HeraldTestConsoleController.php
@@ -1,134 +1,135 @@
<?php
final class HeraldTestConsoleController extends HeraldController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$request = $this->getRequest();
$object_name = trim($request->getStr('object_name'));
$e_name = true;
$errors = array();
if ($request->isFormPost()) {
if (!$object_name) {
$e_name = 'Required';
$errors[] = 'An object name is required.';
}
if (!$errors) {
$matches = null;
$object = null;
if (preg_match('/^D(\d+)$/', $object_name, $matches)) {
$object = id(new DifferentialRevision())->load($matches[1]);
if (!$object) {
$e_name = 'Invalid';
$errors[] = 'No Differential Revision with that ID exists.';
}
} else if (preg_match('/^r([A-Z]+)(\w+)$/', $object_name, $matches)) {
$repo = id(new PhabricatorRepository())->loadOneWhere(
'callsign = %s',
$matches[1]);
if (!$repo) {
$e_name = 'Invalid';
$errors[] = 'There is no repository with the callsign '.
$matches[1].'.';
}
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
'repositoryID = %d AND commitIdentifier = %s',
$repo->getID(),
$matches[2]);
if (!$commit) {
$e_name = 'Invalid';
$errors[] = 'There is no commit with that identifier.';
}
$object = $commit;
} else {
$e_name = 'Invalid';
$errors[] = 'This object name is not recognized.';
}
if (!$errors) {
if ($object instanceof DifferentialRevision) {
$adapter = new HeraldDifferentialRevisionAdapter(
$object,
$object->loadActiveDiff());
} else if ($object instanceof PhabricatorRepositoryCommit) {
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$object->getID());
$adapter = new HeraldCommitAdapter(
$repo,
$object,
$data);
} else {
throw new Exception("Can not build adapter for object!");
}
$rules = HeraldRule::loadAllByContentTypeWithFullData(
$adapter->getHeraldTypeName(),
$object->getPHID());
$engine = new HeraldEngine();
$effects = $engine->applyRules($rules, $adapter);
$dry_run = new HeraldDryRunAdapter();
$engine->applyEffects($effects, $dry_run, $rules);
$xscript = $engine->getTranscript();
return id(new AphrontRedirectResponse())
->setURI('/herald/transcript/'.$xscript->getID().'/');
}
}
}
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setErrors($errors);
} else {
$error_view = null;
}
$form = id(new AphrontFormView())
->setUser($user)
->appendChild(
'<p class="aphront-form-instructions">Enter an object to test rules '.
'for, like a Diffusion commit (e.g., <tt>rX123</tt>) or a '.
'Differential revision (e.g., <tt>D123</tt>). You will be shown the '.
'results of a dry run on the object.</p>')
->appendChild(
id(new AphrontFormTextControl())
- ->setLabel('Object Name')
+ ->setLabel(pht('Object Name'))
->setName('object_name')
->setError($e_name)
->setValue($object_name))
->appendChild(
id(new AphrontFormSubmitControl())
- ->setValue('Test Rules'));
+ ->setValue(pht('Test Rules')));
$panel = new AphrontPanelView();
- $panel->setHeader('Test Herald Rules');
+ $panel->setHeader(pht('Test Herald Rules'));
$panel->setWidth(AphrontPanelView::WIDTH_FULL);
$panel->appendChild($form);
+ $panel->setNoBackground();
$nav = $this->renderNav();
$nav->selectFilter('test');
$nav->appendChild(
array(
$error_view,
$panel,
));
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Test Console',
));
}
}
diff --git a/src/applications/herald/controller/HeraldTranscriptController.php b/src/applications/herald/controller/HeraldTranscriptController.php
index f18e77baf6..496f4eca66 100644
--- a/src/applications/herald/controller/HeraldTranscriptController.php
+++ b/src/applications/herald/controller/HeraldTranscriptController.php
@@ -1,510 +1,511 @@
<?php
final 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!');
}
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 = $this->loadViewerHandles($phids);
$this->handles = $handles;
if ($xscript->getDryRun()) {
$notice = new AphrontErrorView();
$notice->setSeverity(AphrontErrorView::SEVERITY_NOTICE);
$notice->setTitle('Dry Run');
$notice->appendChild(
'This was a dry run to test Herald rules, no actions were executed.');
$nav->appendChild($notice);
}
$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);
}
$main_nav = $this->renderNav();
$main_nav->selectFilter('transcript');
$main_nav->appendChild($nav);
return $this->buildStandardPageResponse(
$main_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 AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI('/herald/transcript/'.$this->id.'/'));
$items = array();
$filters = $this->getFilterMap();
foreach ($filters as $key => $name) {
$nav->addFilter($key, $name);
}
$nav->selectFilter($this->filter, null);
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(null);
$rows = array();
foreach ($xscript->getApplyTranscripts() as $apply_xscript) {
$target = $apply_xscript->getTarget();
switch ($apply_xscript->getAction()) {
case HeraldActionConfig::ACTION_NOTHING:
$target = '';
break;
case HeraldActionConfig::ACTION_FLAG:
$target = PhabricatorFlagColor::getColorName($target);
break;
default:
if ($target) {
foreach ($target as $k => $phid) {
$target[$k] = $handles[$phid]->getName();
}
$target = implode("\n", $target);
} else {
$target = '<empty>';
}
break;
}
$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->setHeader(pht('Actions Taken'));
$panel->appendChild($table);
+ $panel->setNoBackground();
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 = $this->loadViewerHandles(array($phid));
$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;
}
}
diff --git a/src/applications/herald/controller/HeraldTranscriptListController.php b/src/applications/herald/controller/HeraldTranscriptListController.php
index c86fc2dd9b..1f68313e0f 100644
--- a/src/applications/herald/controller/HeraldTranscriptListController.php
+++ b/src/applications/herald/controller/HeraldTranscriptListController.php
@@ -1,108 +1,109 @@
<?php
final class HeraldTranscriptListController extends HeraldController {
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
// Get one page of data together with the pager.
// Pull these objects manually since the serialized fields are gigantic.
$transcript = new HeraldTranscript();
$conn_r = $transcript->establishConnection('r');
$phid = $request->getStr('phid');
$where_clause = '';
if ($phid) {
$where_clause = qsprintf(
$conn_r,
'WHERE objectPHID = %s',
$phid);
}
$pager = new AphrontPagerView();
$pager->setOffset($request->getInt('offset'));
$pager->setURI($request->getRequestURI(), 'offset');
$limit_clause = qsprintf(
$conn_r,
'LIMIT %d, %d',
$pager->getOffset(),
$pager->getPageSize() + 1);
$data = queryfx_all(
$conn_r,
'SELECT id, objectPHID, time, duration, dryRun FROM %T
%Q
ORDER BY id DESC
%Q',
$transcript->getTableName(),
$where_clause,
$limit_clause);
$data = $pager->sliceResults($data);
// Render the table.
$handles = array();
if ($data) {
$phids = ipull($data, 'objectPHID', 'objectPHID');
$handles = $this->loadViewerHandles($phids);
}
$rows = array();
foreach ($data as $xscript) {
$rows[] = array(
phabricator_date($xscript['time'], $user),
phabricator_time($xscript['time'], $user),
$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',
));
// Render the whole page.
$panel = new AphrontPanelView();
- $panel->setHeader('Herald Transcripts');
+ $panel->setHeader(pht('Herald Transcripts'));
$panel->appendChild($table);
$panel->appendChild($pager);
+ $panel->setNoBackground();
$nav = $this->renderNav();
$nav->selectFilter('transcript');
$nav->appendChild($panel);
return $this->buildStandardPageResponse(
$nav,
array(
'title' => 'Herald Transcripts',
));
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Dec 2, 3:04 PM (15 h, 50 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
432162
Default Alt Text
(50 KB)

Event Timeline