public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $xscript = id(new HeraldTranscriptQuery())->setViewer($viewer)->withIDs(array($this->id))->executeOne(); if (!$xscript) { return new Aphront404Response(); } require_celerity_resource('herald-test-css'); $nav = $this->buildSideNav(); $object_xscript = $xscript->getObjectTranscript(); if (!$object_xscript) { $notice = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_NOTICE)->setTitle(pht('Old Transcript'))->appendChild(phutil_tag('p', array(), pht('Details of this transcript have been garbage collected.'))); $nav->appendChild($notice); } else { $map = HeraldAdapter::getEnabledAdapterMap($viewer); $object_type = $object_xscript->getType(); if (empty($map[$object_type])) { // TODO: We should filter these out in the Query, but we have to load // the objectTranscript right now, which is potentially enormous. We // should denormalize the object type, or move the data into a separate // table, and then filter this earlier (and thus raise a better error). // For now, just block access so we don't violate policies. throw new Exception(pht('This transcript has an invalid or inaccessible adapter.')); } $this->adapter = HeraldAdapter::getAdapterForContentType($object_type); $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 PHUIInfoView(); $notice->setSeverity(PHUIInfoView::SEVERITY_NOTICE); $notice->setTitle(pht('Dry Run')); $notice->appendChild(pht('This was a dry run to test Herald rules, ' . 'no actions were executed.')); $nav->appendChild($notice); } $warning_panel = $this->buildWarningPanel($xscript); $nav->appendChild($warning_panel); $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); } $crumbs = id($this->buildApplicationCrumbs())->addTextCrumb(pht('Transcripts'), $this->getApplicationURI('/transcript/'))->addTextCrumb($xscript->getID()); $nav->setCrumbs($crumbs); return $this->buildApplicationPage($nav, array('title' => pht('Transcript'))); }
private function buildPropertySectionView(HeraldRule $rule) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView())->setUser($viewer); $view->addProperty(pht('Rule Type'), idx(HeraldRuleTypeConfig::getRuleTypeMap(), $rule->getRuleType())); if ($rule->isPersonalRule()) { $view->addProperty(pht('Author'), $viewer->renderHandle($rule->getAuthorPHID())); } $adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType()); if ($adapter) { $view->addProperty(pht('Applies To'), idx(HeraldAdapter::getEnabledAdapterMap($viewer), $rule->getContentType())); if ($rule->isObjectRule()) { $view->addProperty(pht('Trigger Object'), $viewer->renderHandle($rule->getTriggerObjectPHID())); } } return $view; }
private function buildPropertyView(HeraldRule $rule, PhabricatorActionListView $actions) { $viewer = $this->getRequest()->getUser(); $view = id(new PHUIPropertyListView())->setUser($viewer)->setObject($rule)->setActionList($actions); $view->addProperty(pht('Rule Type'), idx(HeraldRuleTypeConfig::getRuleTypeMap(), $rule->getRuleType())); if ($rule->isPersonalRule()) { $view->addProperty(pht('Author'), $viewer->renderHandle($rule->getAuthorPHID())); } $adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType()); if ($adapter) { $view->addProperty(pht('Applies To'), idx(HeraldAdapter::getEnabledAdapterMap($viewer), $rule->getContentType())); if ($rule->isObjectRule()) { $view->addProperty(pht('Trigger Object'), $viewer->renderHandle($rule->getTriggerObjectPHID())); } $view->invokeWillRenderEvent(); $view->addSectionHeader(pht('Rule Description'), PHUIPropertyListView::ICON_SUMMARY); $handles = $viewer->loadHandles(HeraldAdapter::getHandlePHIDs($rule)); $view->addTextContent($adapter->renderRuleAsText($rule, $handles)); } return $view; }
private function canAuthorViewObject(HeraldRule $rule, HeraldAdapter $adapter) { // Authorship is irrelevant for global rules and object rules. if ($rule->isGlobalRule() || $rule->isObjectRule()) { return true; } // The author must be able to create rules for the adapter's content type. // In particular, this means that the application must be installed and // accessible to the user. For example, if a user writes a Differential // rule and then loses access to Differential, this disables the rule. $enabled = HeraldAdapter::getEnabledAdapterMap($rule->getAuthor()); if (empty($enabled[$adapter->getAdapterContentType()])) { return false; } // Finally, the author must be able to see the object itself. You can't // write a personal rule that CC's you on revisions you wouldn't otherwise // be able to see, for example. $object = $adapter->getObject(); return PhabricatorPolicyFilter::hasCapability($rule->getAuthor(), $object, PhabricatorPolicyCapability::CAN_VIEW); }
protected function willFilterPage(array $rules) { $rule_ids = mpull($rules, 'getID'); // Filter out any rules that have invalid adapters, or have adapters the // viewer isn't permitted to see or use (for example, Differential rules // if the user can't use Differential or Differential is not installed). $types = HeraldAdapter::getEnabledAdapterMap($this->getViewer()); foreach ($rules as $key => $rule) { if (empty($types[$rule->getContentType()])) { $this->didRejectResult($rule); unset($rules[$key]); } } if ($this->needValidateAuthors) { $this->validateRuleAuthors($rules); } if ($this->needConditionsAndActions) { $conditions = id(new HeraldCondition())->loadAllWhere('ruleID IN (%Ld)', $rule_ids); $conditions = mgroup($conditions, 'getRuleID'); $actions = id(new HeraldAction())->loadAllWhere('ruleID IN (%Ld)', $rule_ids); $actions = mgroup($actions, 'getRuleID'); foreach ($rules as $rule) { $rule->attachActions(idx($actions, $rule->getID(), array())); $rule->attachConditions(idx($conditions, $rule->getID(), array())); } } if ($this->needAppliedToPHIDs) { $conn_r = id(new HeraldRule())->establishConnection('r'); $applied = queryfx_all($conn_r, 'SELECT * FROM %T WHERE ruleID IN (%Ld) AND phid IN (%Ls)', HeraldRule::TABLE_RULE_APPLIED, $rule_ids, $this->needAppliedToPHIDs); $map = array(); foreach ($applied as $row) { $map[$row['ruleID']][$row['phid']] = true; } foreach ($rules as $rule) { foreach ($this->needAppliedToPHIDs as $phid) { $rule->setRuleApplied($phid, isset($map[$rule->getID()][$phid])); } } } $object_phids = array(); foreach ($rules as $rule) { if ($rule->isObjectRule()) { $object_phids[] = $rule->getTriggerObjectPHID(); } } if ($object_phids) { $objects = id(new PhabricatorObjectQuery())->setParentQuery($this)->setViewer($this->getViewer())->withPHIDs($object_phids)->execute(); $objects = mpull($objects, null, 'getPHID'); } else { $objects = array(); } foreach ($rules as $key => $rule) { if ($rule->isObjectRule()) { $object = idx($objects, $rule->getTriggerObjectPHID()); if (!$object) { unset($rules[$key]); continue; } $rule->attachTriggerObject($object); } } return $rules; }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $content_type_map = HeraldAdapter::getEnabledAdapterMap($user); $rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap(); if ($this->id) { $id = $this->id; $rule = id(new HeraldRuleQuery())->setViewer($user)->withIDs(array($id))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne(); if (!$rule) { return new Aphront404Response(); } $cancel_uri = $this->getApplicationURI("rule/{$id}/"); } else { $rule = new HeraldRule(); $rule->setAuthorPHID($user->getPHID()); $rule->setMustMatchAll(1); $content_type = $request->getStr('content_type'); $rule->setContentType($content_type); $rule_type = $request->getStr('rule_type'); if (!isset($rule_type_map[$rule_type])) { $rule_type = HeraldRuleTypeConfig::RULE_TYPE_PERSONAL; } $rule->setRuleType($rule_type); $adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType()); if (!$adapter->supportsRuleType($rule->getRuleType())) { throw new Exception(pht("This rule's content type does not support the selected rule " . "type.")); } if ($rule->isObjectRule()) { $rule->setTriggerObjectPHID($request->getStr('targetPHID')); $object = id(new PhabricatorObjectQuery())->setViewer($user)->withPHIDs(array($rule->getTriggerObjectPHID()))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->executeOne(); if (!$object) { throw new Exception(pht('No valid object provided for object rule!')); } if (!$adapter->canTriggerOnObject($object)) { throw new Exception(pht('Object is of wrong type for adapter!')); } } $cancel_uri = $this->getApplicationURI(); } if ($rule->isGlobalRule()) { $this->requireApplicationCapability(HeraldManageGlobalRulesCapability::CAPABILITY); } $adapter = HeraldAdapter::getAdapterForContentType($rule->getContentType()); $local_version = id(new HeraldRule())->getConfigVersion(); if ($rule->getConfigVersion() > $local_version) { throw new Exception(pht('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($adapter, $rule, $request); if (!$errors) { $id = $rule->getID(); $uri = $this->getApplicationURI("rule/{$id}/"); return id(new AphrontRedirectResponse())->setURI($uri); } } $must_match_selector = $this->renderMustMatchSelector($rule); $repetition_selector = $this->renderRepetitionSelector($rule, $adapter); $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(javelin_tag('input', array('type' => 'hidden', 'name' => 'rule', 'sigil' => 'rule')))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Rule Name'))->setName('name')->setError($e_name)->setValue($rule->getName())); $trigger_object_control = false; if ($rule->isObjectRule()) { $trigger_object_control = id(new AphrontFormStaticControl())->setValue(pht('This rule triggers for %s.', $handles[$rule->getTriggerObjectPHID()]->renderLink())); } $form->appendChild(id(new AphrontFormMarkupControl())->setValue(pht('This %s rule triggers for %s.', phutil_tag('strong', array(), $rule_type_name), phutil_tag('strong', array(), $content_type_name))))->appendChild($trigger_object_control)->appendChild(id(new PHUIFormInsetView())->setTitle(pht('Conditions'))->setRightButton(javelin_tag('a', array('href' => '#', 'class' => 'button green', 'sigil' => 'create-condition', 'mustcapture' => true), pht('New Condition')))->setDescription(pht('When %s these conditions are met:', $must_match_selector))->setContent(javelin_tag('table', array('sigil' => 'rule-conditions', 'class' => 'herald-condition-table'), '')))->appendChild(id(new PHUIFormInsetView())->setTitle(pht('Action'))->setRightButton(javelin_tag('a', array('href' => '#', 'class' => 'button green', 'sigil' => 'create-action', 'mustcapture' => true), pht('New Action')))->setDescription(pht('Take these actions %s this rule matches:', $repetition_selector))->setContent(javelin_tag('table', array('sigil' => 'rule-actions', 'class' => 'herald-action-table'), '')))->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Save Rule'))->addCancelButton($cancel_uri)); $this->setupEditorBehavior($rule, $handles, $adapter); $title = $rule->getID() ? pht('Edit Herald Rule') : pht('Create Herald Rule'); $form_box = id(new PHUIObjectBoxView())->setHeaderText($title)->setFormErrors($errors)->setForm($form); $crumbs = $this->buildApplicationCrumbs()->addTextCrumb($title); return $this->buildApplicationPage(array($crumbs, $form_box), array('title' => pht('Edit Rule'))); }
public function handleRequest(AphrontRequest $request) { $viewer = $request->getViewer(); $content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer); $rule_type_map = HeraldRuleTypeConfig::getRuleTypeMap(); $errors = array(); $e_type = null; $e_rule = null; $e_object = null; $step = $request->getInt('step'); if ($request->isFormPost()) { $content_type = $request->getStr('content_type'); if (empty($content_type_map[$content_type])) { $errors[] = pht('You must choose a content type for this rule.'); $e_type = pht('Required'); $step = 0; } if (!$errors && $step > 1) { $rule_type = $request->getStr('rule_type'); if (empty($rule_type_map[$rule_type])) { $errors[] = pht('You must choose a rule type for this rule.'); $e_rule = pht('Required'); $step = 1; } } if (!$errors && $step >= 2) { $target_phid = null; $object_name = $request->getStr('objectName'); $done = false; if ($rule_type != HeraldRuleTypeConfig::RULE_TYPE_OBJECT) { $done = true; } else { if (strlen($object_name)) { $target_object = id(new PhabricatorObjectQuery())->setViewer($viewer)->withNames(array($object_name))->executeOne(); if ($target_object) { $can_edit = PhabricatorPolicyFilter::hasCapability($viewer, $target_object, PhabricatorPolicyCapability::CAN_EDIT); if (!$can_edit) { $errors[] = pht('You can not create a rule for that object, because you do ' . 'not have permission to edit it. You can only create rules ' . 'for objects you can edit.'); $e_object = pht('Not Editable'); $step = 2; } else { $adapter = HeraldAdapter::getAdapterForContentType($content_type); if (!$adapter->canTriggerOnObject($target_object)) { $errors[] = pht('This object is not of an allowed type for the rule. ' . 'Rules can only trigger on certain objects.'); $e_object = pht('Invalid'); $step = 2; } else { $target_phid = $target_object->getPHID(); $done = true; } } } else { $errors[] = pht('No object exists by that name.'); $e_object = pht('Invalid'); $step = 2; } } else { if ($step > 2) { $errors[] = pht('You must choose an object to associate this rule with.'); $e_object = pht('Required'); $step = 2; } } } if (!$errors && $done) { $uri = id(new PhutilURI('edit/'))->setQueryParams(array('content_type' => $content_type, 'rule_type' => $rule_type, 'targetPHID' => $target_phid)); $uri = $this->getApplicationURI($uri); return id(new AphrontRedirectResponse())->setURI($uri); } } } $content_type = $request->getStr('content_type'); $rule_type = $request->getStr('rule_type'); $form = id(new AphrontFormView())->setUser($viewer)->setAction($this->getApplicationURI('new/')); switch ($step) { case 0: default: $content_types = $this->renderContentTypeControl($content_type_map, $e_type); $form->addHiddenInput('step', 1)->appendChild($content_types); $cancel_text = null; $cancel_uri = $this->getApplicationURI(); break; case 1: $rule_types = $this->renderRuleTypeControl($rule_type_map, $e_rule); $form->addHiddenInput('content_type', $content_type)->addHiddenInput('step', 2)->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Rule for'))->setValue(phutil_tag('strong', array(), idx($content_type_map, $content_type))))->appendChild($rule_types); $cancel_text = pht('Back'); $cancel_uri = id(new PhutilURI('new/'))->setQueryParams(array('content_type' => $content_type, 'step' => 0)); $cancel_uri = $this->getApplicationURI($cancel_uri); break; case 2: $adapter = HeraldAdapter::getAdapterForContentType($content_type); $form->addHiddenInput('content_type', $content_type)->addHiddenInput('rule_type', $rule_type)->addHiddenInput('step', 3)->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Rule for'))->setValue(phutil_tag('strong', array(), idx($content_type_map, $content_type))))->appendChild(id(new AphrontFormStaticControl())->setLabel(pht('Rule Type'))->setValue(phutil_tag('strong', array(), idx($rule_type_map, $rule_type))))->appendRemarkupInstructions(pht('Choose the object this rule will act on (for example, enter ' . '`rX` to act on the `rX` repository, or `#project` to act on ' . 'a project).'))->appendRemarkupInstructions($adapter->explainValidTriggerObjects())->appendChild(id(new AphrontFormTextControl())->setName('objectName')->setError($e_object)->setValue($request->getStr('objectName'))->setLabel(pht('Object'))); $cancel_text = pht('Back'); $cancel_uri = id(new PhutilURI('new/'))->setQueryParams(array('content_type' => $content_type, 'rule_type' => $rule_type, 'step' => 1)); $cancel_uri = $this->getApplicationURI($cancel_uri); break; } $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Continue'))->addCancelButton($cancel_uri, $cancel_text)); $form_box = id(new PHUIObjectBoxView())->setFormErrors($errors)->setHeaderText(pht('Create Herald Rule'))->setForm($form); $crumbs = $this->buildApplicationCrumbs()->addTextCrumb(pht('Create Rule')); return $this->buildApplicationPage(array($crumbs, $form_box), array('title' => pht('Create Herald Rule'))); }
protected function renderResultList(array $rules, PhabricatorSavedQuery $query, array $handles) { assert_instances_of($rules, 'HeraldRule'); $viewer = $this->requireViewer(); $content_type_map = HeraldAdapter::getEnabledAdapterMap($viewer); $list = id(new PHUIObjectItemListView())->setUser($viewer); foreach ($rules as $rule) { $id = $rule->getID(); $item = id(new PHUIObjectItemView())->setObjectName("H{$id}")->setHeader($rule->getName())->setHref($this->getApplicationURI("rule/{$id}/")); if ($rule->isPersonalRule()) { $item->addIcon('fa-user', pht('Personal Rule')); $item->addByline(pht('Authored by %s', $handles[$rule->getAuthorPHID()]->renderLink())); } else { if ($rule->isObjectRule()) { $item->addIcon('fa-briefcase', pht('Object Rule')); } else { $item->addIcon('fa-globe', pht('Global Rule')); } } if ($rule->getIsDisabled()) { $item->setDisabled(true); $item->addIcon('fa-lock grey', pht('Disabled')); } $item->addAction(id(new PHUIListItemView())->setHref($this->getApplicationURI("history/{$id}/"))->setIcon('fa-file-text-o')->setName(pht('Edit Log'))); $content_type_name = idx($content_type_map, $rule->getContentType()); $item->addAttribute(pht('Affects: %s', $content_type_name)); $list->addItem($item); } $result = new PhabricatorApplicationSearchResultView(); $result->setObjectList($list); $result->setNoDataString(pht('No rules found.')); return $result; }