protected function applyAuditors(array $phids, HeraldRule $rule)
 {
     $adapter = $this->getAdapter();
     $object = $adapter->getObject();
     $auditors = $object->getAudits();
     $current = array();
     foreach ($auditors as $auditor) {
         if ($auditor->isInteresting()) {
             $current[] = $auditor->getAuditorPHID();
         }
     }
     $allowed_types = array(PhabricatorPeopleUserPHIDType::TYPECONST, PhabricatorProjectProjectPHIDType::TYPECONST, PhabricatorOwnersPackagePHIDType::TYPECONST);
     $targets = $this->loadStandardTargets($phids, $allowed_types, $current);
     if (!$targets) {
         return;
     }
     $phids = array_fuse(array_keys($targets));
     // TODO: Convert this to be translatable, structured data eventually.
     $reason_map = array();
     foreach ($phids as $phid) {
         $reason_map[$phid][] = pht('%s Triggered Audit', $rule->getMonogram());
     }
     $xaction = $adapter->newTransaction()->setTransactionType(PhabricatorAuditActionConstants::ADD_AUDITORS)->setNewValue($phids)->setMetadataValue('auditStatus', PhabricatorAuditStatusConstants::AUDIT_REQUIRED)->setMetadataValue('auditReasonMap', $reason_map);
     $adapter->queueTransaction($xaction);
     $this->logEffect(self::DO_ADD_AUDITORS, $phids);
 }
 protected function applyRouting(HeraldRule $rule, $route, $phids)
 {
     $adapter = $this->getAdapter();
     $mail = $adapter->getObject();
     $mail->addRoutingRule($route, $phids, $rule->getPHID());
     $this->logEffect(self::DO_ROUTE, array('route' => $route, 'phids' => $phids));
 }
Пример #3
0
 protected function loadPage()
 {
     $table = new HeraldRule();
     $conn_r = $table->establishConnection('r');
     $data = queryfx_all($conn_r, 'SELECT rule.* FROM %T rule %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r));
     return $table->loadAllFromArray($data);
 }
Пример #4
0
 public function execute()
 {
     $table = new HeraldRule();
     $conn_r = $table->establishConnection('r');
     $where = $this->buildWhereClause($conn_r);
     $order = $this->buildOrderClause($conn_r);
     $limit = $this->buildLimitClause($conn_r);
     $data = queryfx_all($conn_r, 'SELECT rule.* FROM %T rule %Q %Q %Q', $table->getTableName(), $where, $order, $limit);
     return $table->loadAllFromArray($data);
 }
 private function buildDescriptionView(HeraldRule $rule)
 {
     $viewer = $this->getRequest()->getUser();
     $view = id(new PHUIPropertyListView())->setUser($viewer);
     $adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType());
     if ($adapter) {
         $handles = $viewer->loadHandles(HeraldAdapter::getHandlePHIDs($rule));
         $rule_text = $adapter->renderRuleAsText($rule, $handles, $viewer);
         $view->addTextContent($rule_text);
         return $view;
     }
     return null;
 }
 private function buildTimeline(HeraldRule $rule)
 {
     $viewer = $this->getRequest()->getUser();
     $xactions = id(new HeraldTransactionQuery())->setViewer($viewer)->withObjectPHIDs(array($rule->getPHID()))->needComments(true)->execute();
     $engine = id(new PhabricatorMarkupEngine())->setViewer($viewer);
     foreach ($xactions as $xaction) {
         if ($xaction->getComment()) {
             $engine->addObject($xaction->getComment(), PhabricatorApplicationTransactionComment::MARKUP_FIELD_COMMENT);
         }
     }
     $engine->process();
     return id(new PhabricatorApplicationTransactionView())->setUser($viewer)->setObjectPHID($rule->getPHID())->setTransactions($xactions)->setMarkupEngine($engine);
 }
 protected function applyBuilds(array $phids, HeraldRule $rule)
 {
     $adapter = $this->getAdapter();
     $allowed_types = array(HarbormasterBuildPlanPHIDType::TYPECONST);
     $targets = $this->loadStandardTargets($phids, $allowed_types, array());
     if (!$targets) {
         return;
     }
     $phids = array_fuse(array_keys($targets));
     foreach ($phids as $phid) {
         $request = id(new HarbormasterBuildRequest())->setBuildPlanPHID($phid)->setInitiatorPHID($rule->getPHID());
         $adapter->queueHarbormasterBuildRequest($request);
     }
     $this->logEffect(self::DO_BUILD, $phids);
 }
 private function buildActionView(HeraldRule $rule)
 {
     $viewer = $this->getRequest()->getUser();
     $id = $rule->getID();
     $view = id(new PhabricatorActionListView())->setUser($viewer)->setObject($rule)->setObjectURI($this->getApplicationURI("rule/{$id}/"));
     $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $rule, PhabricatorPolicyCapability::CAN_EDIT);
     $view->addAction(id(new PhabricatorActionView())->setName(pht('Edit Rule'))->setHref($this->getApplicationURI("edit/{$id}/"))->setIcon('fa-pencil')->setDisabled(!$can_edit)->setWorkflow(!$can_edit));
     if ($rule->getIsDisabled()) {
         $disable_uri = "disable/{$id}/enable/";
         $disable_icon = 'fa-check';
         $disable_name = pht('Activate Rule');
     } else {
         $disable_uri = "disable/{$id}/disable/";
         $disable_icon = 'fa-ban';
         $disable_name = pht('Archive Rule');
     }
     $view->addAction(id(new PhabricatorActionView())->setName(pht('Disable Rule'))->setHref($this->getApplicationURI($disable_uri))->setIcon($disable_icon)->setName($disable_name)->setDisabled(!$can_edit)->setWorkflow(true));
     return $view;
 }
<?php

echo pht("Checking for rules that can be converted to 'personal'.") . "\n";
$table = new HeraldRule();
$table->openTransaction();
$table->beginReadLocking();
$rules = $table->loadAll();
foreach ($rules as $rule) {
    if ($rule->getRuleType() !== HeraldRuleTypeConfig::RULE_TYPE_PERSONAL) {
        $actions = $rule->loadActions();
        $can_be_personal = true;
        foreach ($actions as $action) {
            $target = $action->getTarget();
            if (is_array($target)) {
                if (count($target) > 1) {
                    $can_be_personal = false;
                    break;
                } else {
                    $targetPHID = head($target);
                    if ($targetPHID !== $rule->getAuthorPHID()) {
                        $can_be_personal = false;
                        break;
                    }
                }
            } else {
                if ($target) {
                    if ($target !== $rule->getAuthorPHID()) {
                        $can_be_personal = false;
                        break;
                    }
                }
Пример #10
0
 protected function getRuleEffects(HeraldRule $rule, HeraldObjectAdapter $object)
 {
     $effects = array();
     foreach ($rule->getActions() as $action) {
         $effect = new HeraldEffect();
         $effect->setObjectPHID($object->getPHID());
         $effect->setAction($action->getAction());
         $effect->setTarget($action->getTarget());
         $effect->setRuleID($rule->getID());
         $name = $rule->getName();
         $id = $rule->getID();
         $effect->setReason('Conditions were met for Herald rule "' . $name . '" (#' . $id . ').');
         $effects[] = $effect;
     }
     return $effects;
 }
Пример #11
0
 /**
  * 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)
 {
     $viewer = $this->getRequest()->getUser();
     // Any rule can depend on a global rule.
     $all_rules = id(new HeraldRuleQuery())->setViewer($viewer)->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_GLOBAL))->withContentTypes(array($rule->getContentType()))->execute();
     if ($rule->isObjectRule()) {
         // Object rules may depend on other rules for the same object.
         $all_rules += id(new HeraldRuleQuery())->setViewer($viewer)->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_OBJECT))->withContentTypes(array($rule->getContentType()))->withTriggerObjectPHIDs(array($rule->getTriggerObjectPHID()))->execute();
     }
     if ($rule->isPersonalRule()) {
         // Personal rules may depend upon your other personal rules.
         $all_rules += id(new HeraldRuleQuery())->setViewer($viewer)->withRuleTypes(array(HeraldRuleTypeConfig::RULE_TYPE_PERSONAL))->withContentTypes(array($rule->getContentType()))->withAuthorPHIDs(array($rule->getAuthorPHID()))->execute();
     }
     // mark disabled rules as disabled since they are not useful as such;
     // don't filter though to keep edit cases sane / expected
     foreach ($all_rules as $current_rule) {
         if ($current_rule->getIsDisabled()) {
             $current_rule->makeEphemeral();
             $current_rule->setName($rule->getName() . ' ' . pht('(Disabled)'));
         }
     }
     // A rule can not depend upon itself.
     unset($all_rules[$rule->getID()]);
     return $all_rules;
 }
    public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
    {
        if ($repository->getDetail('herald-disabled')) {
            return;
        }
        $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
        $rules = HeraldRule::loadAllByContentTypeWithFullData(HeraldContentTypeConfig::CONTENT_TYPE_COMMIT);
        $adapter = new HeraldCommitAdapter($repository, $commit, $data);
        $engine = new HeraldEngine();
        $effects = $engine->applyRules($rules, $adapter);
        $engine->applyEffects($effects, $adapter);
        $email_phids = $adapter->getEmailPHIDs();
        if (!$email_phids) {
            return;
        }
        $xscript = $engine->getTranscript();
        $commit_name = $adapter->getHeraldName();
        $revision = $adapter->loadDifferentialRevision();
        $name = null;
        if ($revision) {
            $name = ' ' . $revision->getTitle();
        }
        $author_phid = $data->getCommitDetail('authorPHID');
        $reviewer_phid = $data->getCommitDetail('reviewerPHID');
        $phids = array_filter(array($author_phid, $reviewer_phid));
        $handles = array();
        if ($phids) {
            $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
        }
        if ($author_phid) {
            $author_name = $handles[$author_phid]->getName();
        } else {
            $author_name = $data->getAuthorName();
        }
        if ($reviewer_phid) {
            $reviewer_name = $handles[$reviewer_phid]->getName();
        } else {
            $reviewer_name = null;
        }
        $who = implode(', ', array_filter(array($author_name, $reviewer_name)));
        $description = $data->getCommitMessage();
        $details = PhabricatorEnv::getProductionURI('/' . $commit_name);
        $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.';
        $files = $adapter->loadAffectedPaths();
        sort($files);
        $files = implode("\n  ", $files);
        $xscript_id = $xscript->getID();
        $manage_uri = PhabricatorEnv::getProductionURI('/herald/view/commits/');
        $why_uri = PhabricatorEnv::getProductionURI('/herald/transcript/' . $xscript_id . '/');
        $body = <<<EOBODY
DESCRIPTION
{$description}

DETAILS
  {$details}

DIFFERENTIAL REVISION
  {$differential}

AFFECTED FILES
  {$files}

MANAGE HERALD COMMIT RULES
  {$manage_uri}

WHY DID I GET THIS EMAIL?
  {$why_uri}

EOBODY;
        $subject = "[Herald/Commit] {$commit_name} ({$who}){$name}";
        $mailer = new PhabricatorMetaMTAMail();
        $mailer->setRelatedPHID($commit->getPHID());
        $mailer->addTos($email_phids);
        $mailer->setSubject($subject);
        $mailer->setBody($body);
        $mailer->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader());
        if ($author_phid) {
            $mailer->setFrom($author_phid);
        }
        $mailer->saveAndSend();
    }
 private function queryRules(AphrontPagerView $pager)
 {
     $rule = new HeraldRule();
     $conn_r = $rule->establishConnection('r');
     $where_clause = qsprintf($conn_r, 'WHERE contentType = %s', $this->view);
     if ($this->viewPHID) {
         $where_clause .= qsprintf($conn_r, ' AND authorPHID = %s', $this->viewPHID);
     }
     $data = queryfx_all($conn_r, 'SELECT * FROM %T
     %Q
     ORDER BY id DESC
     LIMIT %d, %d', $rule->getTableName(), $where_clause, $pager->getOffset(), $pager->getPageSize() + 1);
     $data = $pager->sliceResults($data);
     $rules = $rule->loadAllFromArray($data);
     $need_phids = mpull($rules, 'getAuthorPHID');
     if ($this->viewPHID) {
         $need_phids[] = $this->viewPHID;
     }
     $handles = id(new PhabricatorObjectHandleData($need_phids))->loadHandles();
     return array($rules, $handles);
 }
Пример #14
0
<?php

echo pht('Cleaning up old Herald rule applied rows...') . "\n";
$table = new HeraldRule();
$table->openTransaction();
$table->beginReadLocking();
$rules = $table->loadAll();
foreach ($rules as $key => $rule) {
    $first_policy = HeraldRepetitionPolicyConfig::toInt(HeraldRepetitionPolicyConfig::FIRST);
    if ($rule->getRepetitionPolicy() != $first_policy) {
        unset($rules[$key]);
    }
}
$conn_w = $table->establishConnection('w');
$clause = '';
if ($rules) {
    $clause = qsprintf($conn_w, 'WHERE ruleID NOT IN (%Ld)', mpull($rules, 'getID'));
}
echo pht('This may take a moment') . "\n";
do {
    queryfx($conn_w, 'DELETE FROM %T %Q LIMIT 1000', HeraldRule::TABLE_RULE_APPLIED, $clause);
    echo '.';
} while ($conn_w->getAffectedRows());
$table->endReadLocking();
$table->saveTransaction();
echo "\n" . pht('Done.') . "\n";
Пример #15
0
 /**
  * Given a @{class:HeraldRule}, this function extracts all the phids that
  * we'll want to load as handles later.
  *
  * This function performs a somewhat hacky approach to figuring out what
  * is and is not a phid - try to get the phid type and if the type is
  * *not* unknown assume its a valid phid.
  *
  * Don't try this at home. Use more strongly typed data at home.
  *
  * Think of the children.
  */
 public static function getHandlePHIDs(HeraldRule $rule)
 {
     $phids = array($rule->getAuthorPHID());
     foreach ($rule->getConditions() as $condition) {
         $value = $condition->getValue();
         if (!is_array($value)) {
             $value = array($value);
         }
         foreach ($value as $val) {
             if (phid_get_type($val) != PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
                 $phids[] = $val;
             }
         }
     }
     foreach ($rule->getActions() as $action) {
         $target = $action->getTarget();
         if (!is_array($target)) {
             $target = array($target);
         }
         foreach ($target as $val) {
             if (phid_get_type($val) != PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
                 $phids[] = $val;
             }
         }
     }
     if ($rule->isObjectRule()) {
         $phids[] = $rule->getTriggerObjectPHID();
     }
     return $phids;
 }
Пример #16
0
 public function serializeRule(HeraldRule $rule)
 {
     return $this->serializeRuleComponents((bool) $rule->getMustMatchAll(), $rule->getConditions(), $rule->getActions(), HeraldRepetitionPolicyConfig::toString($rule->getRepetitionPolicy()));
 }
Пример #17
0
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $content_type_map = HeraldContentTypeConfig::getContentTypeMap();
     if ($this->id) {
         $rule = id(new HeraldRule())->load($this->id);
         if (!$rule) {
             return new Aphront404Response();
         }
         if ($rule->getAuthorPHID() != $user->getPHID() && !$user->getIsAdmin()) {
             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);
         $type = $request->getStr('type');
         if (!isset($content_type_map[$type])) {
             $type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
         }
         $rule->setContentType($type);
     }
     $this->setFilter($rule->getContentType());
     $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. Try dev or wait for a push.");
     }
     // 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() . '/';
             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->getMustMatchSelector($rule);
     $repetition_selector = $this->getRepetitionSelector($rule);
     $handles = $this->loadHandles($rule);
     require_celerity_resource('herald-css');
     $type_name = $content_type_map[$rule->getContentType()];
     $form = id(new AphrontFormView())->setUser($user)->setID('herald-rule-edit-form')->addHiddenInput('type', $rule->getContentType())->addHiddenInput('save', 1)->appendChild(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()))->appendChild(id(new AphrontFormStaticControl())->setLabel('Author')->setValue($handles[$rule->getAuthorPHID()]->getName()))->appendChild(id(new AphrontFormMarkupControl())->setValue("This rule triggers for <strong>{$type_name}</strong>."))->appendChild('<h1>Conditions</h1>' . '<div class="aphront-form-inset">' . '<div style="float: right;">' . javelin_render_tag('a', array('href' => '#', 'class' => 'button green', 'sigil' => 'create-condition', 'mustcapture' => true), 'Create New Condition') . '</div>' . '<p>When ' . $must_match_selector . ' these conditions are met:</p>' . '<div style="clear: both;"></div>' . javelin_render_tag('table', array('sigil' => 'rule-conditions', 'class' => 'herald-condition-table'), '') . '</div>')->appendChild('<h1>Action</h1>' . '<div class="aphront-form-inset">' . '<div style="float: right;">' . javelin_render_tag('a', array('href' => '#', 'class' => 'button green', 'sigil' => 'create-action', 'mustcapture' => true), 'Create New Action') . '</div>' . '<p>' . 'Take these actions ' . $repetition_selector . ' this rule matches:' . '</p>' . '<div style="clear: both;"></div>' . javelin_render_tag('table', array('sigil' => 'rule-actions', 'class' => 'herald-action-table'), '') . '</div>')->appendChild(id(new AphrontFormSubmitControl())->setValue('Save Rule')->addCancelButton('/herald/view/' . $rule->getContentType() . '/'));
     $this->setupEditorBehavior($rule, $handles);
     $panel = new AphrontPanelView();
     $panel->setHeader('Edit Herald Rule');
     $panel->setWidth(AphrontPanelView::WIDTH_WIDE);
     $panel->appendChild($form);
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Edit Rule'));
 }
 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());
                 $engine = new HeraldEngine();
                 $effects = $engine->applyRules($rules, $adapter);
                 $dry_run = new HeraldDryRunAdapter();
                 $engine->applyEffects($effects, $dry_run);
                 $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')->setName('object_name')->setError($e_name)->setValue($object_name))->appendChild(id(new AphrontFormSubmitControl())->setValue('Test Rules'));
     $panel = new AphrontPanelView();
     $panel->setHeader('Test Herald Rules');
     $panel->setWidth(AphrontPanelView::WIDTH_FULL);
     $panel->appendChild($form);
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Test Console', 'tab' => 'test'));
 }
Пример #19
0
 /**
  * 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;
 }
Пример #20
0
<?php

$table = new HeraldRule();
$conn_w = $table->establishConnection('w');
echo "Assigning PHIDs to Herald Rules...\n";
foreach (new LiskMigrationIterator(new HeraldRule()) as $rule) {
    $id = $rule->getID();
    echo "Rule {$id}.\n";
    if ($rule->getPHID()) {
        continue;
    }
    queryfx($conn_w, 'UPDATE %T SET phid = %s WHERE id = %d', $table->getTableName(), PhabricatorPHID::generateNewPHID(HeraldRulePHIDType::TYPECONST), $rule->getID());
}
echo "Done.\n";
 public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
 {
     $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
     if (!$data) {
         // TODO: Permanent failure.
         return;
     }
     $rules = HeraldRule::loadAllByContentTypeWithFullData(HeraldContentTypeConfig::CONTENT_TYPE_COMMIT, $commit->getPHID());
     $adapter = new HeraldCommitAdapter($repository, $commit, $data);
     $engine = new HeraldEngine();
     $effects = $engine->applyRules($rules, $adapter);
     $engine->applyEffects($effects, $adapter, $rules);
     $audit_phids = $adapter->getAuditMap();
     if ($audit_phids) {
         $this->createAudits($commit, $audit_phids, $rules);
     }
     $explicit_auditors = $this->createAuditsFromCommitMessage($commit, $data);
     if ($repository->getDetail('herald-disabled')) {
         // This just means "disable email"; audits are (mostly) idempotent.
         return;
     }
     $this->publishFeedStory($repository, $commit, $data);
     $herald_targets = $adapter->getEmailPHIDs();
     $email_phids = array_unique(array_merge($explicit_auditors, $herald_targets));
     if (!$email_phids) {
         return;
     }
     $xscript = $engine->getTranscript();
     $revision = $adapter->loadDifferentialRevision();
     if ($revision) {
         $name = $revision->getTitle();
     } else {
         $name = $data->getSummary();
     }
     $author_phid = $data->getCommitDetail('authorPHID');
     $reviewer_phid = $data->getCommitDetail('reviewerPHID');
     $phids = array_filter(array($author_phid, $reviewer_phid, $commit->getPHID()));
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     $commit_handle = $handles[$commit->getPHID()];
     $commit_name = $commit_handle->getName();
     if ($author_phid) {
         $author_name = $handles[$author_phid]->getName();
     } else {
         $author_name = $data->getAuthorName();
     }
     if ($reviewer_phid) {
         $reviewer_name = $handles[$reviewer_phid]->getName();
     } else {
         $reviewer_name = null;
     }
     $who = implode(', ', array_filter(array($author_name, $reviewer_name)));
     $description = $data->getCommitMessage();
     $commit_uri = PhabricatorEnv::getProductionURI($commit_handle->getURI());
     $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.';
     $files = $adapter->loadAffectedPaths();
     sort($files);
     $files = implode("\n", $files);
     $xscript_id = $xscript->getID();
     $manage_uri = '/herald/view/commits/';
     $why_uri = '/herald/transcript/' . $xscript_id . '/';
     $reply_handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit($commit);
     $template = new PhabricatorMetaMTAMail();
     $inline_patch_text = $this->buildPatch($template, $repository, $commit);
     $body = new PhabricatorMetaMTAMailBody();
     $body->addRawSection($description);
     $body->addTextSection(pht('DETAILS'), $commit_uri);
     $body->addTextSection(pht('DIFFERENTIAL REVISION'), $differential);
     $body->addTextSection(pht('AFFECTED FILES'), $files);
     $body->addReplySection($reply_handler->getReplyHandlerInstructions());
     $body->addHeraldSection($manage_uri, $why_uri);
     $body->addRawSection($inline_patch_text);
     $body = $body->render();
     $prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix');
     $threading = PhabricatorAuditCommentEditor::getMailThreading($repository, $commit);
     list($thread_id, $thread_topic) = $threading;
     $template->setRelatedPHID($commit->getPHID());
     $template->setSubject("{$commit_name}: {$name}");
     $template->setSubjectPrefix($prefix);
     $template->setVarySubjectPrefix("[Commit]");
     $template->setBody($body);
     $template->setThreadID($thread_id, $is_new = true);
     $template->addHeader('Thread-Topic', $thread_topic);
     $template->setIsBulk(true);
     $template->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader());
     if ($author_phid) {
         $template->setFrom($author_phid);
     }
     $mails = $reply_handler->multiplexMail($template, id(new PhabricatorObjectHandleData($email_phids))->loadHandles(), array());
     foreach ($mails as $mail) {
         $mail->saveAndSend();
     }
 }
 public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $content_type_map = HeraldContentTypeConfig::getContentTypeMap();
     if ($this->id) {
         $rule = id(new HeraldRule())->load($this->id);
         if (!$rule) {
             return new Aphront404Response();
         }
         if ($rule->getAuthorPHID() != $user->getPHID()) {
             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);
         $type = $request->getStr('type');
         if (!isset($content_type_map[$type])) {
             $type = HeraldContentTypeConfig::CONTENT_TYPE_DIFFERENTIAL;
         }
         $rule->setContentType($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. Try dev or wait for a push.");
     }
     // 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')) {
         $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));
         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;
             }
             $obj = new HeraldAction();
             $obj->setAction($action[0]);
             if (!isset($action[1])) {
                 // Legitimate for any action which doesn't need a target, like
                 // "Do nothing".
                 $action[1] = null;
             }
             if (is_array($action[1])) {
                 $obj->setTarget(array_keys($action[1]));
             } else {
                 $obj->setTarget($action[1]);
             }
             $actions[] = $obj;
         }
         $rule->attachConditions($conditions);
         $rule->attachActions($actions);
         if (!$errors) {
             try {
                 // TODO
                 //          $rule->openTransaction();
                 $rule->save();
                 $rule->saveConditions($conditions);
                 $rule->saveActions($actions);
                 //          $rule->saveTransaction();
                 $uri = '/herald/view/' . $rule->getContentType() . '/';
                 return id(new AphrontRedirectResponse())->setURI($uri);
             } catch (AphrontQueryDuplicateKeyException $ex) {
                 $e_name = "Not Unique";
                 $errors[] = "Rule name is not unique. Choose a unique name.";
             }
         }
     }
     $phids = array();
     $phids[] = $rule->getAuthorPHID();
     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;
             }
         }
     }
     $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
     if ($errors) {
         $error_view = new AphrontErrorView();
         $error_view->setTitle('Form Errors');
         $error_view->setErrors($errors);
     } else {
         $error_view = null;
     }
     $options = array('all' => 'all of', 'any' => 'any of');
     $selected = $rule->getMustMatchAll() ? 'all' : 'any';
     $must_match = array();
     foreach ($options as $key => $option) {
         $must_match[] = phutil_render_tag('option', array('selected' => $selected == $key ? 'selected' : null, 'value' => $key), phutil_escape_html($option));
     }
     $must_match = '<select name="must_match">' . implode("\n", $must_match) . '</select>';
     if ($rule->getID()) {
         $action = '/herald/rule/' . $rule->getID() . '/';
     } else {
         $action = '/herald/rule/' . $rule->getID() . '/';
     }
     // Make the selector for choosing how often this rule should be repeated
     $repetition_selector = "";
     $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);
     } else {
         if (count($repetition_options) == 1) {
             // if there's only 1 option, just pick it for the user
             $repetition_selector = reset($repetition_options);
         } else {
             // give the user all the options for this rule type
             $tags = array();
             foreach ($repetition_options as $name => $option) {
                 $tags[] = phutil_render_tag('option', array('selected' => $repetition_policy == $name ? 'selected' : null, 'value' => $name), phutil_escape_html($option));
             }
             $repetition_selector = '<select name="repetition_policy">' . implode("\n", $tags) . '</select>';
         }
     }
     require_celerity_resource('herald-css');
     $type_name = $content_type_map[$rule->getContentType()];
     $form = id(new AphrontFormView())->setUser($user)->setID('herald-rule-edit-form')->addHiddenInput('type', $rule->getContentType())->addHiddenInput('save', 1)->appendChild(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()))->appendChild(id(new AphrontFormStaticControl())->setLabel('Author')->setValue($handles[$rule->getAuthorPHID()]->getName()))->appendChild(id(new AphrontFormMarkupControl())->setValue("This rule triggers for <strong>{$type_name}</strong>."))->appendChild('<h1>Conditions</h1>' . '<div class="aphront-form-inset">' . '<div style="float: right;">' . javelin_render_tag('a', array('href' => '#', 'class' => 'button green', 'sigil' => 'create-condition', 'mustcapture' => true), 'Create New Condition') . '</div>' . '<p>When ' . $must_match . ' these conditions are met:</p>' . '<div style="clear: both;"></div>' . javelin_render_tag('table', array('sigil' => 'rule-conditions', 'class' => 'herald-condition-table'), '') . '</div>')->appendChild('<h1>Action</h1>' . '<div class="aphront-form-inset">' . '<div style="float: right;">' . javelin_render_tag('a', array('href' => '#', 'class' => 'button green', 'sigil' => 'create-action', 'mustcapture' => true), 'Create New Action') . '</div>' . '<p>' . 'Take these actions ' . $repetition_selector . ' this rule matches:' . '</p>' . '<div style="clear: both;"></div>' . javelin_render_tag('table', array('sigil' => 'rule-actions', 'class' => 'herald-action-table'), '') . '</div>')->appendChild(id(new AphrontFormSubmitControl())->setValue('Save Rule')->addCancelButton('/herald/view/' . $rule->getContentType() . '/'));
     $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) {
             $target_map = array();
             foreach ((array) $action->getTarget() as $fbid) {
                 $target_map[$fbid] = $handles[$fbid]->getName();
             }
             $serial_actions[] = array($action->getAction(), $target_map);
         }
     }
     $all_rules = id(new HeraldRule())->loadAllWhere('authorPHID = %d AND contentType = %s', $rule->getAuthorPHID(), $rule->getContentType());
     $all_rules = mpull($all_rules, 'getName', 'getID');
     asort($all_rules);
     unset($all_rules[$rule->getID()]);
     $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::getActionMapForContentType($rule->getContentType());
     foreach ($config_info['actions'] as $action => $name) {
         $config_info['targets'][$action] = HeraldValueTypeConfig::getValueTypeForAction($action);
     }
     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), 'info' => $config_info));
     $panel = new AphrontPanelView();
     $panel->setHeader('Edit Herald Rule');
     $panel->setWidth(AphrontPanelView::WIDTH_WIDE);
     $panel->appendChild($form);
     return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Edit Rule'));
 }
Пример #23
0
 private function canRuleApplyToObject(HeraldRule $rule, HeraldAdapter $adapter)
 {
     // Rules which are not object rules can apply to anything.
     if (!$rule->isObjectRule()) {
         return true;
     }
     $trigger_phid = $rule->getTriggerObjectPHID();
     $object_phids = $adapter->getTriggerObjectPHIDs();
     if ($object_phids) {
         if (in_array($trigger_phid, $object_phids)) {
             return true;
         }
     }
     return false;
 }
    public function parseCommit(PhabricatorRepository $repository, PhabricatorRepositoryCommit $commit)
    {
        $data = id(new PhabricatorRepositoryCommitData())->loadOneWhere('commitID = %d', $commit->getID());
        if (!$data) {
            // TODO: Permanent failure.
            return;
        }
        $rules = HeraldRule::loadAllByContentTypeWithFullData(HeraldContentTypeConfig::CONTENT_TYPE_COMMIT, $commit->getPHID());
        $adapter = new HeraldCommitAdapter($repository, $commit, $data);
        $engine = new HeraldEngine();
        $effects = $engine->applyRules($rules, $adapter);
        $engine->applyEffects($effects, $adapter, $rules);
        $audit_phids = $adapter->getAuditMap();
        if ($audit_phids) {
            $this->createAudits($commit, $audit_phids, $rules);
        }
        $this->createAuditsFromCommitMessage($commit, $data);
        $email_phids = $adapter->getEmailPHIDs();
        if (!$email_phids) {
            return;
        }
        if ($repository->getDetail('herald-disabled')) {
            // This just means "disable email"; audits are (mostly) idempotent.
            return;
        }
        $xscript = $engine->getTranscript();
        $revision = $adapter->loadDifferentialRevision();
        if ($revision) {
            $name = $revision->getTitle();
        } else {
            $name = $data->getSummary();
        }
        $author_phid = $data->getCommitDetail('authorPHID');
        $reviewer_phid = $data->getCommitDetail('reviewerPHID');
        $phids = array_filter(array($author_phid, $reviewer_phid, $commit->getPHID()));
        $handles = id(new PhabricatorObjectHandleData($phids))->loadHandles();
        $commit_handle = $handles[$commit->getPHID()];
        $commit_name = $commit_handle->getName();
        if ($author_phid) {
            $author_name = $handles[$author_phid]->getName();
        } else {
            $author_name = $data->getAuthorName();
        }
        if ($reviewer_phid) {
            $reviewer_name = $handles[$reviewer_phid]->getName();
        } else {
            $reviewer_name = null;
        }
        $who = implode(', ', array_filter(array($author_name, $reviewer_name)));
        $description = $data->getCommitMessage();
        $commit_uri = PhabricatorEnv::getProductionURI($commit_handle->getURI());
        $differential = $revision ? PhabricatorEnv::getProductionURI('/D' . $revision->getID()) : 'No revision.';
        $files = $adapter->loadAffectedPaths();
        sort($files);
        $files = implode("\n  ", $files);
        $xscript_id = $xscript->getID();
        $manage_uri = PhabricatorEnv::getProductionURI('/herald/view/commits/');
        $why_uri = PhabricatorEnv::getProductionURI('/herald/transcript/' . $xscript_id . '/');
        $reply_handler = PhabricatorAuditCommentEditor::newReplyHandlerForCommit($commit);
        $reply_instructions = $reply_handler->getReplyHandlerInstructions();
        if ($reply_instructions) {
            $reply_instructions = "\n" . "REPLY HANDLER ACTIONS\n" . "  " . $reply_instructions . "\n";
        }
        $body = <<<EOBODY
DESCRIPTION
{$description}

DETAILS
  {$commit_uri}

DIFFERENTIAL REVISION
  {$differential}

AFFECTED FILES
  {$files}
{$reply_instructions}
MANAGE HERALD COMMIT RULES
  {$manage_uri}

WHY DID I GET THIS EMAIL?
  {$why_uri}

EOBODY;
        $prefix = PhabricatorEnv::getEnvConfig('metamta.diffusion.subject-prefix');
        $subject = trim("{$prefix} {$commit_name}: {$name}");
        $vary_subject = trim("{$prefix} [Commit] {$commit_name}: {$name}");
        $threading = PhabricatorAuditCommentEditor::getMailThreading($commit->getPHID());
        list($thread_id, $thread_topic) = $threading;
        $template = new PhabricatorMetaMTAMail();
        $template->setRelatedPHID($commit->getPHID());
        $template->setSubject($subject);
        $template->setVarySubject($subject);
        $template->setBody($body);
        $template->setThreadID($thread_id, $is_new = true);
        $template->addHeader('Thread-Topic', $thread_topic);
        $template->setIsBulk(true);
        $template->addHeader('X-Herald-Rules', $xscript->getXHeraldRulesHeader());
        if ($author_phid) {
            $template->setFrom($author_phid);
        }
        $mails = $reply_handler->multiplexMail($template, id(new PhabricatorObjectHandleData($email_phids))->loadHandles(), array());
        foreach ($mails as $mail) {
            $mail->saveAndSend();
        }
    }