diff --git a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php index 19b9fc44fb..f944b8b171 100644 --- a/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php +++ b/src/applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php @@ -1,41 +1,41 @@ setName('stop') ->setSynopsis(pht('Stop daemon processes on this host.')) ->setArguments( array( array( 'name' => 'graceful', 'param' => 'seconds', 'help' => pht( 'Grace period for daemons to attempt a clean shutdown, in '. 'seconds. Defaults to __15__ seconds.'), 'default' => 15, ), array( 'name' => 'force', 'help' => pht( 'Stop all daemon processes on this host, even if they belong '. - 'to another Phabricator instance.'), + 'to another instance.'), ), array( 'name' => 'gently', 'help' => pht('Deprecated. Has no effect.'), ), )); } public function execute(PhutilArgumentParser $args) { return $this->executeStopCommand( array( 'graceful' => $args->getArg('graceful'), 'force' => $args->getArg('force'), )); } } diff --git a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php index 8969151c06..52e8cc70d5 100644 --- a/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php +++ b/src/applications/dashboard/engine/PhabricatorDashboardPanelRenderingEngine.php @@ -1,452 +1,452 @@ contextObject = $object; return $this; } public function getContextObject() { return $this->contextObject; } public function setPanelKey($panel_key) { $this->panelKey = $panel_key; return $this; } public function getPanelKey() { return $this->panelKey; } public function setHeaderMode($header_mode) { $this->headerMode = $header_mode; return $this; } public function getHeaderMode() { return $this->headerMode; } public function setPanelHandle(PhabricatorObjectHandle $panel_handle) { $this->panelHandle = $panel_handle; return $this; } public function getPanelHandle() { return $this->panelHandle; } public function isEditMode() { return $this->editMode; } public function setEditMode($mode) { $this->editMode = $mode; return $this; } /** * Allow the engine to render the panel via Ajax. */ public function setEnableAsyncRendering($enable) { $this->enableAsyncRendering = $enable; return $this; } public function setParentPanelPHIDs(array $parents) { $this->parentPanelPHIDs = $parents; return $this; } public function getParentPanelPHIDs() { return $this->parentPanelPHIDs; } public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; return $this; } public function getViewer() { return $this->viewer; } public function setPanel(PhabricatorDashboardPanel $panel) { $this->panel = $panel; return $this; } public function setMovable($movable) { $this->movable = $movable; return $this; } public function getMovable() { return $this->movable; } public function getPanel() { return $this->panel; } public function setPanelPHID($panel_phid) { $this->panelPHID = $panel_phid; return $this; } public function getPanelPHID() { return $this->panelPHID; } public function renderPanel() { $panel = $this->getPanel(); if (!$panel) { $handle = $this->getPanelHandle(); if ($handle->getPolicyFiltered()) { return $this->renderErrorPanel( pht('Restricted Panel'), pht( 'You do not have permission to see this panel.')); } else { return $this->renderErrorPanel( pht('Invalid Panel'), pht( 'This panel is invalid or does not exist. It may have been '. 'deleted.')); } } $panel_type = $panel->getImplementation(); if (!$panel_type) { return $this->renderErrorPanel( $panel->getName(), pht( - 'This panel has type "%s", but that panel type is not known to '. - 'Phabricator.', + 'This panel has type "%s", but that panel type is unknown.', $panel->getPanelType())); } try { $this->detectRenderingCycle($panel); if ($this->enableAsyncRendering) { if ($panel_type->shouldRenderAsync()) { return $this->renderAsyncPanel(); } } return $this->renderNormalPanel(); } catch (Exception $ex) { return $this->renderErrorPanel( $panel->getName(), pht( '%s: %s', phutil_tag('strong', array(), get_class($ex)), $ex->getMessage())); } } private function renderNormalPanel() { $panel = $this->getPanel(); $panel_type = $panel->getImplementation(); $content = $panel_type->renderPanelContent( $this->getViewer(), $panel, $this); $header = $this->renderPanelHeader(); return $this->renderPanelDiv( $content, $header); } private function renderAsyncPanel() { $context_phid = $this->getContextPHID(); $panel = $this->getPanel(); $panel_id = celerity_generate_unique_node_id(); Javelin::initBehavior( 'dashboard-async-panel', array( 'panelID' => $panel_id, 'parentPanelPHIDs' => $this->getParentPanelPHIDs(), 'headerMode' => $this->getHeaderMode(), 'contextPHID' => $context_phid, 'panelKey' => $this->getPanelKey(), 'movable' => $this->getMovable(), 'uri' => '/dashboard/panel/render/'.$panel->getID().'/', )); $header = $this->renderPanelHeader(); $content = id(new PHUIPropertyListView()) ->addTextContent(pht('Loading...')); return $this->renderPanelDiv( $content, $header, $panel_id); } private function renderErrorPanel($title, $body) { switch ($this->getHeaderMode()) { case self::HEADER_MODE_NONE: $header = null; break; case self::HEADER_MODE_EDIT: $header = id(new PHUIHeaderView()) ->setHeader($title); $header = $this->addPanelHeaderActions($header); break; case self::HEADER_MODE_NORMAL: default: $header = id(new PHUIHeaderView()) ->setHeader($title); break; } $icon = id(new PHUIIconView()) ->setIcon('fa-warning red msr'); $content = id(new PHUIBoxView()) ->addClass('dashboard-box') ->addMargin(PHUI::MARGIN_LARGE) ->appendChild($icon) ->appendChild($body); return $this->renderPanelDiv( $content, $header); } private function renderPanelDiv( $content, $header = null, $id = null) { require_celerity_resource('phabricator-dashboard-css'); $panel = $this->getPanel(); if (!$id) { $id = celerity_generate_unique_node_id(); } $box = new PHUIObjectBoxView(); $interface = 'PhabricatorApplicationSearchResultView'; if ($content instanceof $interface) { if ($content->getObjectList()) { $box->setObjectList($content->getObjectList()); } if ($content->getTable()) { $box->setTable($content->getTable()); } if ($content->getContent()) { $box->appendChild($content->getContent()); } } else { $box->appendChild($content); } $box ->setHeader($header) ->setID($id) ->addClass('dashboard-box') ->addSigil('dashboard-panel'); if ($this->getMovable()) { $box->addSigil('panel-movable'); } if ($panel) { $box->setMetadata( array( 'panelKey' => $this->getPanelKey(), )); } return $box; } private function renderPanelHeader() { $panel = $this->getPanel(); switch ($this->getHeaderMode()) { case self::HEADER_MODE_NONE: $header = null; break; case self::HEADER_MODE_EDIT: // In edit mode, include the panel monogram to make managing boards // a little easier. $header_text = pht('%s %s', $panel->getMonogram(), $panel->getName()); $header = id(new PHUIHeaderView()) ->setHeader($header_text); $header = $this->addPanelHeaderActions($header); break; case self::HEADER_MODE_NORMAL: default: $header = id(new PHUIHeaderView()) ->setHeader($panel->getName()); $panel_type = $panel->getImplementation(); $header = $panel_type->adjustPanelHeader( $this->getViewer(), $panel, $this, $header); break; } return $header; } private function addPanelHeaderActions( PHUIHeaderView $header) { $viewer = $this->getViewer(); $panel = $this->getPanel(); $context_phid = $this->getContextPHID(); $actions = array(); if ($panel) { try { $panel_actions = $panel->newHeaderEditActions( $viewer, $context_phid); } catch (Exception $ex) { $error_action = id(new PhabricatorActionView()) ->setIcon('fa-exclamation-triangle red') ->setName(pht('')); $panel_actions[] = $error_action; } if ($panel_actions) { foreach ($panel_actions as $panel_action) { $actions[] = $panel_action; } $actions[] = id(new PhabricatorActionView()) ->setType(PhabricatorActionView::TYPE_DIVIDER); } $panel_id = $panel->getID(); $edit_uri = "/dashboard/panel/edit/{$panel_id}/"; $params = array( 'contextPHID' => $context_phid, ); $edit_uri = new PhutilURI($edit_uri, $params); $actions[] = id(new PhabricatorActionView()) ->setIcon('fa-pencil') ->setName(pht('Edit Panel')) ->setHref($edit_uri); $actions[] = id(new PhabricatorActionView()) ->setIcon('fa-window-maximize') ->setName(pht('View Panel Details')) ->setHref($panel->getURI()); } if ($context_phid) { $panel_phid = $this->getPanelPHID(); $remove_uri = urisprintf('/dashboard/adjust/remove/'); $params = array( 'contextPHID' => $context_phid, 'panelKey' => $this->getPanelKey(), ); $remove_uri = new PhutilURI($remove_uri, $params); $actions[] = id(new PhabricatorActionView()) ->setIcon('fa-times') ->setHref($remove_uri) ->setName(pht('Remove Panel')) ->setWorkflow(true); } $dropdown_menu = id(new PhabricatorActionListView()) ->setViewer($viewer); foreach ($actions as $action) { $dropdown_menu->addAction($action); } $action_menu = id(new PHUIButtonView()) ->setTag('a') ->setIcon('fa-cog') ->setText(pht('Manage Panel')) ->setDropdownMenu($dropdown_menu); $header->addActionLink($action_menu); return $header; } /** * Detect graph cycles in panels, and deeply nested panels. * * This method throws if the current rendering stack is too deep or contains * a cycle. This can happen if you embed layout panels inside each other, * build a big stack of panels, or embed a panel in remarkup inside another * panel. Generally, all of this stuff is ridiculous and we just want to * shut it down. * * @param PhabricatorDashboardPanel Panel being rendered. * @return void */ private function detectRenderingCycle(PhabricatorDashboardPanel $panel) { if ($this->parentPanelPHIDs === null) { throw new PhutilInvalidStateException('setParentPanelPHIDs'); } $max_depth = 4; if (count($this->parentPanelPHIDs) >= $max_depth) { throw new Exception( pht( 'To render more than %s levels of panels nested inside other '. - 'panels, purchase a subscription to Phabricator Gold.', - new PhutilNumber($max_depth))); + 'panels, purchase a subscription to %s Gold.', + new PhutilNumber($max_depth), + PlatformSymbols::getPlatformServerName())); } if (in_array($panel->getPHID(), $this->parentPanelPHIDs)) { throw new Exception( pht( 'You awake in a twisting maze of mirrors, all alike. '. 'You are likely to be eaten by a graph cycle. '. 'Should you escape alive, you resolve to be more careful about '. 'putting dashboard panels inside themselves.')); } } private function getContextPHID() { $context = $this->getContextObject(); if ($context) { return $context->getPHID(); } return null; } } diff --git a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php index 4173b208ba..61cc2bbdf2 100644 --- a/src/applications/dashboard/storage/PhabricatorDashboardPanel.php +++ b/src/applications/dashboard/storage/PhabricatorDashboardPanel.php @@ -1,185 +1,184 @@ setName('') ->setAuthorPHID($actor->getPHID()) ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) ->setEditPolicy($actor->getPHID()); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'properties' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'name' => 'sort255', 'panelType' => 'text64', 'authorPHID' => 'phid', 'isArchived' => 'bool', ), ) + parent::getConfiguration(); } public function getPHIDType() { return PhabricatorDashboardPanelPHIDType::TYPECONST; } public function getProperty($key, $default = null) { return idx($this->properties, $key, $default); } public function setProperty($key, $value) { $this->properties[$key] = $value; return $this; } public function getMonogram() { return 'W'.$this->getID(); } public function getURI() { return '/'.$this->getMonogram(); } public function getPanelTypes() { $panel_types = PhabricatorDashboardPanelType::getAllPanelTypes(); $panel_types = mpull($panel_types, 'getPanelTypeName', 'getPanelTypeKey'); asort($panel_types); $panel_types = (array('' => pht('(All Types)')) + $panel_types); return $panel_types; } public function getStatuses() { $statuses = array( '' => pht('(All Panels)'), 'active' => pht('Active Panels'), 'archived' => pht('Archived Panels'), ); return $statuses; } public function getImplementation() { return idx( PhabricatorDashboardPanelType::getAllPanelTypes(), $this->getPanelType()); } public function requireImplementation() { $impl = $this->getImplementation(); if (!$impl) { throw new Exception( pht( 'Attempting to use a panel in a way that requires an '. - 'implementation, but the panel implementation ("%s") is unknown to '. - 'Phabricator.', + 'implementation, but the panel implementation ("%s") is unknown.', $this->getPanelType())); } return $impl; } public function getEditEngineFields() { return $this->requireImplementation()->getEditEngineFields($this); } public function newHeaderEditActions( PhabricatorUser $viewer, $context_phid) { return $this->requireImplementation()->newHeaderEditActions( $this, $viewer, $context_phid); } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new PhabricatorDashboardPanelTransactionEditor(); } public function getApplicationTransactionTemplate() { return new PhabricatorDashboardPanelTransaction(); } /* -( PhabricatorPolicyInterface )----------------------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return false; } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $this->saveTransaction(); } /* -( PhabricatorDashboardPanelContainerInterface )------------------------ */ public function getDashboardPanelContainerPanelPHIDs() { return $this->requireImplementation()->getSubpanelPHIDs($this); } /* -( PhabricatorFulltextInterface )--------------------------------------- */ public function newFulltextEngine() { return new PhabricatorDashboardPanelFulltextEngine(); } /* -( PhabricatorFerretInterface )----------------------------------------- */ public function newFerretEngine() { return new PhabricatorDashboardPanelFerretEngine(); } } diff --git a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php index 9634756dac..8c33bd5fad 100644 --- a/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php +++ b/src/applications/differential/config/PhabricatorDifferentialConfigOptions.php @@ -1,254 +1,254 @@ getFieldKey()] = array( 'disabled' => $field->shouldDisableByDefault(), ); } $inline_description = $this->deformat( pht(<<newOption( 'differential.fields', $custom_field_type, $default_fields) ->setCustomData( id(new DifferentialRevision())->getCustomFieldBaseClass()) ->setDescription( pht( "Select and reorder revision fields.\n\n". "NOTE: This feature is under active development and subject ". "to change.")), $this->newOption('differential.require-test-plan-field', 'bool', true) ->setBoolOptions( array( pht("Require 'Test Plan' field"), pht("Make 'Test Plan' field optional"), )) ->setSummary(pht('Require "Test Plan" field?')) ->setDescription( pht( "Differential has a required 'Test Plan' field by default. You ". "can make it optional by setting this to false. You can also ". "completely remove it above, if you prefer.")), $this->newOption('differential.enable-email-accept', 'bool', false) ->setBoolOptions( array( pht('Enable Email "!accept" Action'), pht('Disable Email "!accept" Action'), )) ->setSummary(pht('Enable or disable "!accept" action via email.')) ->setDescription( pht( 'If inbound email is configured, users can interact with '. 'revisions by using "!actions" in email replies (for example, '. '"!resign" or "!rethink"). However, by default, users may not '. '"!accept" revisions via email: email authentication can be '. 'configured to be very weak, and email "!accept" is kind of '. 'sketchy and implies the revision may not actually be receiving '. 'thorough review. You can enable "!accept" by setting this '. 'option to true.')), $this->newOption('differential.generated-paths', 'list', array()) ->setSummary(pht('File regexps to treat as automatically generated.')) ->setDescription( pht( 'List of file regexps that should be treated as if they are '. 'generated by an automatic process, and thus be hidden by '. 'default in Differential.'. "\n\n". 'NOTE: This property is cached, so you will need to purge the '. 'cache after making changes if you want the new configuration '. 'to affect existing revisions. For instructions, see '. '**[[ %s | Managing Caches ]]** in the documentation.', $caches_href)) ->addExample("/config\.h$/\n#(^|/)autobuilt/#", pht('Valid Setting')), $this->newOption('differential.sticky-accept', 'bool', true) ->setBoolOptions( array( pht('Accepts persist across updates'), pht('Accepts are reset by updates'), )) ->setSummary( pht('Should "Accepted" revisions remain "Accepted" after updates?')) ->setDescription( pht( 'Normally, when revisions that have been "Accepted" are updated, '. 'they remain "Accepted". This allows reviewers to suggest minor '. 'alterations when accepting, and encourages authors to update '. 'if they make minor changes in response to this feedback.'. "\n\n". 'If you want updates to always require re-review, you can disable '. 'the "stickiness" of the "Accepted" status with this option. '. 'This may make the process for minor changes much more burdensome '. 'to both authors and reviewers.')), $this->newOption('differential.allow-self-accept', 'bool', false) ->setBoolOptions( array( pht('Allow self-accept'), pht('Disallow self-accept'), )) ->setSummary(pht('Allows users to accept their own revisions.')) ->setDescription( pht( "If you set this to true, users can accept their own revisions. ". "This action is disabled by default because it's most likely not ". "a behavior you want, but it proves useful if you are working ". "alone on a project and want to make use of all of ". "differential's features.")), $this->newOption('differential.always-allow-close', 'bool', false) ->setBoolOptions( array( pht('Allow any user'), pht('Restrict to submitter'), )) ->setSummary(pht('Allows any user to close accepted revisions.')) ->setDescription( pht( 'If you set this to true, any user can close any revision so '. 'long as it has been accepted. This can be useful depending on '. 'your development model. For example, github-style pull requests '. 'where the reviewer is often the actual committer can benefit '. 'from turning this option to true. If false, only the submitter '. 'can close a revision.')), $this->newOption('differential.always-allow-abandon', 'bool', false) ->setBoolOptions( array( pht('Allow any user'), pht('Restrict to submitter'), )) ->setSummary(pht('Allows any user to abandon revisions.')) ->setDescription( pht( 'If you set this to true, any user can abandon any revision. If '. 'false, only the submitter can abandon a revision.')), $this->newOption('differential.allow-reopen', 'bool', false) ->setBoolOptions( array( pht('Enable reopen'), pht('Disable reopen'), )) ->setSummary(pht('Allows any user to reopen a closed revision.')) ->setDescription( pht( 'If you set this to true, any user can reopen a revision so '. 'long as it has been closed. This can be useful if a revision '. 'is accidentally closed or if a developer changes his or her '. 'mind after closing a revision. If it is false, reopening '. 'is not allowed.')), $this->newOption('differential.close-on-accept', 'bool', false) ->setBoolOptions( array( pht('Treat Accepted Revisions as "Closed"'), pht('Treat Accepted Revisions as "Open"'), )) ->setSummary(pht('Allows "Accepted" to act as a closed status.')) ->setDescription( pht( 'Normally, Differential revisions remain on the dashboard when '. 'they are "Accepted", and the author then commits the changes '. 'to "Close" the revision and move it off the dashboard.'. "\n\n". 'If you have an unusual workflow where Differential is used for '. - 'post-commit review (normally called "Audit", elsewhere in '. - 'Phabricator), you can set this flag to treat the "Accepted" '. + 'post-commit review (normally called "Audit", elsewhere), you '. + 'can set this flag to treat the "Accepted" '. 'state as a "Closed" state and end the review workflow early.'. "\n\n". 'This sort of workflow is very unusual. Very few installs should '. 'need to change this option.')), $this->newOption( 'metamta.differential.attach-patches', 'bool', false) ->setBoolOptions( array( pht('Attach Patches'), pht('Do Not Attach Patches'), )) ->setSummary(pht('Attach patches to email, as text attachments.')) ->setDescription( pht( - 'If you set this to true, Phabricator will attach patches to '. + 'If you set this to true, patches will be attached to '. 'Differential mail (as text attachments). This will not work if '. 'you are using SendGrid as your mail adapter.')), $this->newOption( 'metamta.differential.inline-patches', 'int', 0) ->setSummary(pht('Inline patches in email, as body text.')) ->setDescription($inline_description), $this->newOption( 'metamta.differential.patch-format', 'enum', 'unified') ->setDescription( pht('Format for inlined or attached patches.')) ->setEnumOptions( array( 'unified' => pht('Unified'), 'git' => pht('Git'), )), ); } } diff --git a/src/applications/differential/controller/DifferentialDiffCreateController.php b/src/applications/differential/controller/DifferentialDiffCreateController.php index f9d1006555..c3bfc01743 100644 --- a/src/applications/differential/controller/DifferentialDiffCreateController.php +++ b/src/applications/differential/controller/DifferentialDiffCreateController.php @@ -1,218 +1,219 @@ getViewer(); // If we're on the "Update Diff" workflow, load the revision we're going // to update. $revision = null; $revision_id = $request->getURIData('revisionID'); if ($revision_id) { $revision = id(new DifferentialRevisionQuery()) ->setViewer($viewer) ->withIDs(array($revision_id)) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$revision) { return new Aphront404Response(); } } $diff = null; // This object is just for policy stuff $diff_object = DifferentialDiff::initializeNewDiff($viewer); if ($revision) { $repository_phid = $revision->getRepositoryPHID(); } else { $repository_phid = null; } $errors = array(); $e_diff = null; $e_file = null; $validation_exception = null; if ($request->isFormPost()) { $repository_tokenizer = $request->getArr( id(new DifferentialRepositoryField())->getFieldKey()); if ($repository_tokenizer) { $repository_phid = reset($repository_tokenizer); } if ($request->getFileExists('diff-file')) { $diff = PhabricatorFile::readUploadedFileData($_FILES['diff-file']); } else { $diff = $request->getStr('diff'); } if (!strlen($diff)) { $errors[] = pht( 'You can not create an empty diff. Paste a diff or upload a '. 'file containing a diff.'); $e_diff = pht('Required'); $e_file = pht('Required'); } if (!$errors) { try { $call = new ConduitCall( 'differential.createrawdiff', array( 'diff' => $diff, 'repositoryPHID' => $repository_phid, 'viewPolicy' => $request->getStr('viewPolicy'), )); $call->setUser($viewer); $result = $call->execute(); $diff_id = $result['id']; $uri = $this->getApplicationURI("diff/{$diff_id}/"); $uri = new PhutilURI($uri); if ($revision) { $uri->replaceQueryParam('revisionID', $revision->getID()); } return id(new AphrontRedirectResponse())->setURI($uri); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; } } } $form = new AphrontFormView(); $arcanist_href = PhabricatorEnv::getDoclink('Arcanist User Guide'); $arcanist_link = phutil_tag( 'a', array( 'href' => $arcanist_href, 'target' => '_blank', ), pht('Learn More')); $cancel_uri = $this->getApplicationURI(); $policies = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->setObject($diff_object) ->execute(); $info_view = null; if (!$request->isFormPost()) { $info_view = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NOTICE) ->setErrors( array( array( pht( - 'The best way to create a diff is to use the Arcanist '. - 'command-line tool.'), + 'The best way to create a diff is to use the %s '. + 'command-line tool.', + PlatformSymbols::getPlatformClientName()), ' ', $arcanist_link, ), pht( 'You can also paste a diff above, or upload a file '. 'containing a diff (for example, from %s, %s or %s).', phutil_tag('tt', array(), 'svn diff'), phutil_tag('tt', array(), 'git diff'), phutil_tag('tt', array(), 'hg diff --git')), )); } if ($revision) { $title = pht('Update Diff'); $header = pht('Update Diff'); $button = pht('Continue'); $header_icon = 'fa-upload'; } else { $title = pht('Create Diff'); $header = pht('Create New Diff'); $button = pht('Create Diff'); $header_icon = 'fa-plus-square'; } $form ->setEncType('multipart/form-data') ->setUser($viewer); if ($revision) { $form->appendChild( id(new AphrontFormMarkupControl()) ->setLabel(pht('Updating Revision')) ->setValue($viewer->renderHandle($revision->getPHID()))); } if ($repository_phid) { $repository_value = array($repository_phid); } else { $repository_value = array(); } $form ->appendChild( id(new AphrontFormTextAreaControl()) ->setLabel(pht('Raw Diff')) ->setName('diff') ->setValue($diff) ->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL) ->setError($e_diff)) ->appendChild( id(new AphrontFormFileControl()) ->setLabel(pht('Raw Diff From File')) ->setName('diff-file') ->setError($e_file)) ->appendControl( id(new AphrontFormTokenizerControl()) ->setName(id(new DifferentialRepositoryField())->getFieldKey()) ->setLabel(pht('Repository')) ->setDatasource(new DiffusionRepositoryDatasource()) ->setValue($repository_value) ->setLimit(1)) ->appendChild( id(new AphrontFormPolicyControl()) ->setUser($viewer) ->setName('viewPolicy') ->setPolicyObject($diff_object) ->setPolicies($policies) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW)) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($cancel_uri) ->setValue($button)); $form_box = id(new PHUIObjectBoxView()) ->setHeaderText($title) ->setValidationException($validation_exception) ->setForm($form) ->setBackground(PHUIObjectBoxView::WHITE_CONFIG) ->setFormErrors($errors); $crumbs = $this->buildApplicationCrumbs(); if ($revision) { $crumbs->addTextCrumb( $revision->getMonogram(), '/'.$revision->getMonogram()); } $crumbs->addTextCrumb($title); $crumbs->setBorder(true); $view = id(new PHUITwoColumnView()) ->setFooter(array( $form_box, $info_view, )); return $this->newPage() ->setTitle($title) ->setCrumbs($crumbs) ->appendChild($view); } } diff --git a/src/applications/differential/customfield/DifferentialJIRAIssuesField.php b/src/applications/differential/customfield/DifferentialJIRAIssuesField.php index e97efd6ad5..8bd0b052dd 100644 --- a/src/applications/differential/customfield/DifferentialJIRAIssuesField.php +++ b/src/applications/differential/customfield/DifferentialJIRAIssuesField.php @@ -1,285 +1,286 @@ getValue()); } public function setValueFromStorage($value) { try { $this->setValue(phutil_json_decode($value)); } catch (PhutilJSONParserException $ex) { $this->setValue(array()); } return $this; } public function getFieldName() { return pht('JIRA Issues'); } public function getFieldDescription() { return pht('Lists associated JIRA issues.'); } public function shouldAppearInPropertyView() { return true; } public function renderPropertyViewLabel() { return $this->getFieldName(); } public function renderPropertyViewValue(array $handles) { $xobjs = $this->loadDoorkeeperExternalObjects($this->getValue()); if (!$xobjs) { return null; } $links = array(); foreach ($xobjs as $xobj) { $links[] = id(new DoorkeeperTagView()) ->setExternalObject($xobj); } return phutil_implode_html(phutil_tag('br'), $links); } private function buildDoorkeeperRefs($value) { $provider = PhabricatorJIRAAuthProvider::getJIRAProvider(); $refs = array(); if ($value) { foreach ($value as $jira_key) { $refs[] = id(new DoorkeeperObjectRef()) ->setApplicationType(DoorkeeperBridgeJIRA::APPTYPE_JIRA) ->setApplicationDomain($provider->getProviderDomain()) ->setObjectType(DoorkeeperBridgeJIRA::OBJTYPE_ISSUE) ->setObjectID($jira_key); } } return $refs; } private function loadDoorkeeperExternalObjects($value) { $refs = $this->buildDoorkeeperRefs($value); if (!$refs) { return array(); } $xobjs = id(new DoorkeeperExternalObjectQuery()) ->setViewer($this->getViewer()) ->withObjectKeys(mpull($refs, 'getObjectKey')) ->execute(); return $xobjs; } public function shouldAppearInEditView() { return PhabricatorJIRAAuthProvider::getJIRAProvider(); } public function shouldAppearInApplicationTransactions() { return PhabricatorJIRAAuthProvider::getJIRAProvider(); } public function readValueFromRequest(AphrontRequest $request) { $this->setValue($request->getStrList($this->getFieldKey())); return $this; } public function renderEditControl(array $handles) { return id(new AphrontFormTextControl()) ->setLabel(pht('JIRA Issues')) ->setCaption( pht('Example: %s', phutil_tag('tt', array(), 'JIS-3, JIS-9'))) ->setName($this->getFieldKey()) ->setValue(implode(', ', nonempty($this->getValue(), array()))) ->setError($this->error); } public function getOldValueForApplicationTransactions() { return array_unique(nonempty($this->getValue(), array())); } public function getNewValueForApplicationTransactions() { return array_unique(nonempty($this->getValue(), array())); } public function validateApplicationTransactions( PhabricatorApplicationTransactionEditor $editor, $type, array $xactions) { $this->error = null; $errors = parent::validateApplicationTransactions( $editor, $type, $xactions); $transaction = null; foreach ($xactions as $xaction) { $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $add = array_diff($new, $old); if (!$add) { continue; } // Only check that the actor can see newly added JIRA refs. You're // allowed to remove refs or make no-op changes even if you aren't // linked to JIRA. try { $refs = id(new DoorkeeperImportEngine()) ->setViewer($this->getViewer()) ->setRefs($this->buildDoorkeeperRefs($add)) ->setThrowOnMissingLink(true) ->execute(); } catch (DoorkeeperMissingLinkException $ex) { $this->error = pht('Not Linked'); $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Not Linked'), pht( 'You can not add JIRA issues (%s) to this revision because your '. - 'Phabricator account is not linked to a JIRA account.', - implode(', ', $add)), + '%s account is not linked to a JIRA account.', + implode(', ', $add), + PlatformSymbols::getPlatformServerName()), $xaction); continue; } $bad = array(); foreach ($refs as $ref) { if (!$ref->getIsVisible()) { $bad[] = $ref->getObjectID(); } } if ($bad) { $bad = implode(', ', $bad); $this->error = pht('Invalid'); $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht( 'Some JIRA issues could not be loaded. They may not exist, or '. 'you may not have permission to view them: %s', $bad), $xaction); } } return $errors; } public function getApplicationTransactionTitle( PhabricatorApplicationTransaction $xaction) { $old = $xaction->getOldValue(); if (!is_array($old)) { $old = array(); } $new = $xaction->getNewValue(); if (!is_array($new)) { $new = array(); } $add = array_diff($new, $old); $rem = array_diff($old, $new); $author_phid = $xaction->getAuthorPHID(); if ($add && $rem) { return pht( '%s updated JIRA issue(s): added %d %s; removed %d %s.', $xaction->renderHandleLink($author_phid), phutil_count($add), implode(', ', $add), phutil_count($rem), implode(', ', $rem)); } else if ($add) { return pht( '%s added %d JIRA issue(s): %s.', $xaction->renderHandleLink($author_phid), phutil_count($add), implode(', ', $add)); } else if ($rem) { return pht( '%s removed %d JIRA issue(s): %s.', $xaction->renderHandleLink($author_phid), phutil_count($rem), implode(', ', $rem)); } return parent::getApplicationTransactionTitle($xaction); } public function applyApplicationTransactionExternalEffects( PhabricatorApplicationTransaction $xaction) { // Update the CustomField storage. parent::applyApplicationTransactionExternalEffects($xaction); // Now, synchronize the Doorkeeper edges. $revision = $this->getObject(); $revision_phid = $revision->getPHID(); $edge_type = PhabricatorJiraIssueHasObjectEdgeType::EDGECONST; $xobjs = $this->loadDoorkeeperExternalObjects($xaction->getNewValue()); $edge_dsts = mpull($xobjs, 'getPHID'); $edges = PhabricatorEdgeQuery::loadDestinationPHIDs( $revision_phid, $edge_type); $editor = new PhabricatorEdgeEditor(); foreach (array_diff($edges, $edge_dsts) as $rem_edge) { $editor->removeEdge($revision_phid, $edge_type, $rem_edge); } foreach (array_diff($edge_dsts, $edges) as $add_edge) { $editor->addEdge($revision_phid, $edge_type, $add_edge); } $editor->save(); } public function shouldAppearInConduitDictionary() { return true; } public function shouldAppearInConduitTransactions() { return true; } protected function newConduitEditParameterType() { return new ConduitStringListParameterType(); } } diff --git a/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php index 88d216e4a1..4d651b4e52 100644 --- a/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionLookSoonConduitAPIMethod.php @@ -1,61 +1,61 @@ 'optional list (deprecated)', 'repositories' => 'optional list', 'urgency' => 'optional string', ); } protected function execute(ConduitAPIRequest $request) { // NOTE: The "urgency" parameter does nothing, it is just a hilarious joke // which exemplifies the boundless clever wit of this project. $identifiers = $request->getValue('repositories'); if (!$identifiers) { $identifiers = $request->getValue('callsigns'); } if (!$identifiers) { return null; } $repositories = id(new PhabricatorRepositoryQuery()) ->setViewer($request->getUser()) ->withIdentifiers($identifiers) ->execute(); foreach ($repositories as $repository) { $repository->writeStatusMessage( PhabricatorRepositoryStatusMessage::TYPE_NEEDS_UPDATE, PhabricatorRepositoryStatusMessage::CODE_OKAY); } return null; } } diff --git a/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php b/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php index be889b4cc4..f1451be247 100644 --- a/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php +++ b/src/applications/diffusion/config/PhabricatorDiffusionConfigOptions.php @@ -1,166 +1,165 @@ getFieldKey()] = array( 'disabled' => $field->shouldDisableByDefault(), ); } return array( $this->newOption( 'metamta.diffusion.attach-patches', 'bool', false) ->setBoolOptions( array( pht('Attach Patches'), pht('Do Not Attach Patches'), )) ->setDescription( pht( 'Set this to true if you want patches to be attached to commit '. 'notifications from Diffusion.')), $this->newOption('metamta.diffusion.inline-patches', 'int', 0) ->setSummary(pht('Include patches in Diffusion mail as body text.')) ->setDescription( pht( 'To include patches in Diffusion email bodies, set this to a '. 'positive integer. Patches will be inlined if they are at most '. 'that many lines. By default, patches are not inlined.')), $this->newOption('metamta.diffusion.byte-limit', 'int', 1024 * 1024) ->setDescription(pht('Hard byte limit on including patches in email.')), $this->newOption('metamta.diffusion.time-limit', 'int', 60) ->setDescription(pht('Hard time limit on generating patches.')), $this->newOption( 'audit.can-author-close-audit', 'bool', false) ->setBoolOptions( array( pht('Enable Self-Accept'), pht('Disable Self-Accept'), )) ->setDescription( pht( 'Allows the author of a commit to be an auditor and accept their '. 'own commits. Note that this behavior is different from the '. 'behavior implied by the name of the option: long ago, it did '. 'something else.')), $this->newOption('bugtraq.url', 'string', null) ->addExample('https://bugs.php.net/%BUGID%', pht('PHP bugs')) ->addExample('/%BUGID%', pht('Local Maniphest URL')) ->setDescription( pht( 'URL of external bug tracker used by Diffusion. %s will be '. 'substituted by the bug ID.', '%BUGID%')), $this->newOption('bugtraq.logregex', 'list', array()) ->addExample(array('/\B#([1-9]\d*)\b/'), pht('Issue #123')) ->addExample( array('/[Ii]ssues?:?(\s*,?\s*#\d+)+/', '/(\d+)/'), pht('Issue #123, #456')) ->addExample(array('/(?addExample('/[A-Z]{2,}-\d+/', pht('JIRA-1234')) ->setDescription( pht( 'Regular expression to link external bug tracker. See '. 'http://tortoisesvn.net/docs/release/TortoiseSVN_en/'. 'tsvn-dug-bugtracker.html for further explanation.')), $this->newOption('diffusion.allow-http-auth', 'bool', false) ->setBoolOptions( array( pht('Allow HTTP Basic Auth'), pht('Disallow HTTP Basic Auth'), )) ->setSummary(pht('Enable HTTP Basic Auth for repositories.')) ->setDescription( pht( - "Phabricator can serve repositories over HTTP, using HTTP basic ". + "This server can serve repositories over HTTP, using HTTP basic ". "auth.\n\n". "Because HTTP basic auth is less secure than SSH auth, it is ". "disabled by default. You can enable it here if you'd like to use ". "it anyway. There's nothing fundamentally insecure about it as ". - "long as Phabricator uses HTTPS, but it presents a much lower ". + "long as this server uses HTTPS, but it presents a much lower ". "barrier to attackers than SSH does.\n\n". "Consider using SSH for authenticated access to repositories ". "instead of HTTP.")), $this->newOption('diffusion.allow-git-lfs', 'bool', false) ->setBoolOptions( array( pht('Allow Git LFS'), pht('Disallow Git LFS'), )) ->setLocked(true) ->setSummary(pht('Allow Git Large File Storage (LFS).')) ->setDescription( pht( - 'Phabricator supports Git LFS, a Git extension for storing large '. + 'This server supports Git LFS, a Git extension for storing large '. 'files alongside a repository. Activate this setting to allow '. - 'the extension to store file data in Phabricator.')), + 'the extension to store file data.')), $this->newOption('diffusion.ssh-user', 'string', null) ->setLocked(true) ->setSummary(pht('Login username for SSH connections to repositories.')) ->setDescription( pht( 'When constructing clone URIs to show to users, Diffusion will '. 'fill in this login username. If you have configured a VCS user '. 'like `git`, you should provide it here.')), $this->newOption('diffusion.ssh-port', 'int', null) ->setLocked(true) ->setSummary(pht('Port for SSH connections to repositories.')) ->setDescription( pht( 'When constructing clone URIs to show to users, Diffusion by '. 'default will not display a port assuming the default for your '. 'VCS. Explicitly declare when running on a non-standard port.')), $this->newOption('diffusion.ssh-host', 'string', null) ->setLocked(true) ->setSummary(pht('Host for SSH connections to repositories.')) ->setDescription( pht( - 'If you accept Phabricator SSH traffic on a different host '. - 'from web traffic (for example, if you use different SSH and '. - 'web load balancers), you can set the SSH hostname here. This '. - 'is an advanced option.')), + 'If you accept SSH traffic on a different host from web traffic '. + '(for example, if you use different SSH and web load balancers), '. + 'you can set the SSH hostname here. This is an advanced option.')), $this->newOption('diffusion.fields', $custom_field_type, $default_fields) ->setCustomData( id(new PhabricatorRepositoryCommit()) ->getCustomFieldBaseClass()) ->setDescription( pht('Select and reorder Diffusion fields.')), ); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php index 9503a9e386..50f1d34da1 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditDangerousController.php @@ -1,88 +1,88 @@ loadDiffusionContextForEdit(); if ($response) { return $response; } $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $panel_uri = id(new DiffusionRepositoryBasicsManagementPanel()) ->setRepository($repository) ->getPanelURI(); if (!$repository->canAllowDangerousChanges()) { return $this->newDialog() ->setTitle(pht('Unprotectable Repository')) ->appendParagraph( pht( 'This repository can not be protected from dangerous changes '. - 'because Phabricator does not control what users are allowed '. + 'because this server does not control what users are allowed '. 'to push to it.')) ->addCancelButton($panel_uri); } if ($request->isFormPost()) { $xaction = id(new PhabricatorRepositoryTransaction()) ->setTransactionType( PhabricatorRepositoryDangerousTransaction::TRANSACTIONTYPE) ->setNewValue(!$repository->shouldAllowDangerousChanges()); $editor = id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, array($xaction)); return id(new AphrontReloadResponse())->setURI($panel_uri); } $force = phutil_tag('tt', array(), '--force'); if ($repository->shouldAllowDangerousChanges()) { $title = pht('Prevent Dangerous Changes'); if ($repository->isSVN()) { $body = pht( 'It will no longer be possible to edit revprops in this '. 'repository.'); } else { $body = pht( 'It will no longer be possible to delete branches from this '. 'repository, or %s push to this repository.', $force); } $submit = pht('Prevent Dangerous Changes'); } else { $title = pht('Allow Dangerous Changes'); if ($repository->isSVN()) { $body = pht( 'If you allow dangerous changes, it will be possible to edit '. 'reprops in this repository, including arbitrarily rewriting '. 'commit messages. These operations can alter a repository in a '. 'way that is difficult to recover from.'); } else { $body = pht( 'If you allow dangerous changes, it will be possible to delete '. 'branches and %s push this repository. These operations can '. 'alter a repository in a way that is difficult to recover from.', $force); } $submit = pht('Allow Dangerous Changes'); } return $this->newDialog() ->setTitle($title) ->appendParagraph($body) ->addSubmitButton($submit) ->addCancelButton($panel_uri); } } diff --git a/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php b/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php index 11a3ee3736..3e0b56a758 100644 --- a/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php +++ b/src/applications/diffusion/controller/DiffusionRepositoryEditEnormousController.php @@ -1,91 +1,91 @@ loadDiffusionContextForEdit(); if ($response) { return $response; } $viewer = $this->getViewer(); $drequest = $this->getDiffusionRequest(); $repository = $drequest->getRepository(); $panel_uri = id(new DiffusionRepositoryBasicsManagementPanel()) ->setRepository($repository) ->getPanelURI(); if (!$repository->canAllowEnormousChanges()) { return $this->newDialog() ->setTitle(pht('Unprotectable Repository')) ->appendParagraph( pht( 'This repository can not be protected from enormous changes '. - 'because Phabricator does not control what users are allowed '. + 'because this server does not control what users are allowed '. 'to push to it.')) ->addCancelButton($panel_uri); } if ($request->isFormPost()) { $xaction = id(new PhabricatorRepositoryTransaction()) ->setTransactionType( PhabricatorRepositoryEnormousTransaction::TRANSACTIONTYPE) ->setNewValue(!$repository->shouldAllowEnormousChanges()); $editor = id(new PhabricatorRepositoryEditor()) ->setContinueOnNoEffect(true) ->setContentSourceFromRequest($request) ->setActor($viewer) ->applyTransactions($repository, array($xaction)); return id(new AphrontReloadResponse())->setURI($panel_uri); } if ($repository->shouldAllowEnormousChanges()) { $title = pht('Prevent Enormous Changes'); $body = pht( 'It will no longer be possible to push enormous changes to this '. 'repository.'); $submit = pht('Prevent Enormous Changes'); } else { $title = pht('Allow Enormous Changes'); $body = array( pht( 'If you allow enormous changes, users can push commits which are '. 'too large for Herald to process content rules for. This can allow '. 'users to evade content rules implemented in Herald.'), pht( 'You can selectively configure Herald by adding rules to prevent a '. 'subset of enormous changes (for example, based on who is trying '. 'to push the change).'), ); $submit = pht('Allow Enormous Changes'); } $more_help = pht( 'Enormous changes are commits which are too large to process with '. 'content rules because: the diff text for the change is larger than '. '%s bytes; or the diff text takes more than %s seconds to extract.', new PhutilNumber(HeraldCommitAdapter::getEnormousByteLimit()), new PhutilNumber(HeraldCommitAdapter::getEnormousTimeLimit())); $response = $this->newDialog(); foreach ((array)$body as $paragraph) { $response->appendParagraph($paragraph); } return $response ->setTitle($title) ->appendParagraph($more_help) ->addSubmitButton($submit) ->addCancelButton($panel_uri); } }