diff --git a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php index cf0104af82..6df7369ba2 100644 --- a/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php +++ b/src/applications/almanac/management/AlmanacManagementRegisterWorkflow.php @@ -1,221 +1,221 @@ setName('register') ->setSynopsis(pht('Register this host as an Almanac device.')) ->setArguments( array( array( 'name' => 'device', 'param' => 'name', 'help' => pht('Almanac device name to register.'), ), array( 'name' => 'private-key', 'param' => 'key', 'help' => pht('Path to a private key for the host.'), ), array( 'name' => 'allow-key-reuse', 'help' => pht( 'Register even if another host is already registered with this '. 'keypair. This is an advanced featuer which allows a pool of '. 'devices to share credentials.'), ), array( 'name' => 'identify-as', 'param' => 'name', 'help' => pht( 'Specify an alternate host identity. This is an advanced '. 'feature which allows a pool of devices to share credentials.'), ), array( 'name' => 'force', 'help' => pht( 'Register this host even if keys already exist.'), ), )); } public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $device_name = $args->getArg('device'); if (!strlen($device_name)) { throw new PhutilArgumentUsageException( pht('Specify a device with --device.')); } $device = id(new AlmanacDeviceQuery()) ->setViewer($this->getViewer()) ->withNames(array($device_name)) ->executeOne(); if (!$device) { throw new PhutilArgumentUsageException( pht('No such device "%s" exists!', $device_name)); } $private_key_path = $args->getArg('private-key'); if (!strlen($private_key_path)) { throw new PhutilArgumentUsageException( pht('Specify a private key with --private-key.')); } if (!Filesystem::pathExists($private_key_path)) { throw new PhutilArgumentUsageException( pht('Private key "%s" does not exist!', $private_key_path)); } $raw_private_key = Filesystem::readFile($private_key_path); $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); if (!$phd_user) { throw new PhutilArgumentUsageException( pht( 'Config option "phd.user" is not set. You must set this option '. 'so the private key can be stored with the correct permissions.')); } $tmp = new TempFile(); list($err) = exec_manual('chown %s %s', $phd_user, $tmp); if ($err) { throw new PhutilArgumentUsageException( pht( 'Unable to change ownership of a file to daemon user "%s". Run '. 'this command as %s or root.', $phd_user, $phd_user)); } $stored_public_path = AlmanacKeys::getKeyPath('device.pub'); $stored_private_path = AlmanacKeys::getKeyPath('device.key'); $stored_device_path = AlmanacKeys::getKeyPath('device.id'); if (!$args->getArg('force')) { if (Filesystem::pathExists($stored_public_path)) { throw new PhutilArgumentUsageException( pht( 'This host already has a registered public key ("%s"). '. 'Remove this key before registering the host, or use '. '--force to overwrite it.', Filesystem::readablePath($stored_public_path))); } if (Filesystem::pathExists($stored_private_path)) { throw new PhutilArgumentUsageException( pht( 'This host already has a registered private key ("%s"). '. 'Remove this key before registering the host, or use '. '--force to overwrite it.', Filesystem::readablePath($stored_private_path))); } } // NOTE: We're writing the private key here so we can change permissions // on it without causing weird side effects to the file specified with // the `--private-key` flag. The file needs to have restrictive permissions // before `ssh-keygen` will willingly operate on it. $tmp_private = new TempFile(); Filesystem::changePermissions($tmp_private, 0600); execx('chown %s %s', $phd_user, $tmp_private); Filesystem::writeFile($tmp_private, $raw_private_key); list($raw_public_key) = execx('ssh-keygen -y -f %s', $tmp_private); $key_object = PhabricatorAuthSSHPublicKey::newFromRawKey($raw_public_key); $public_key = id(new PhabricatorAuthSSHKeyQuery()) ->setViewer($this->getViewer()) ->withKeys(array($key_object)) ->executeOne(); if ($public_key) { if ($public_key->getObjectPHID() !== $device->getPHID()) { throw new PhutilArgumentUsageException( pht( 'The public key corresponding to the given private key is '. 'already associated with an object other than the specified '. 'device. You can not use a single private key to identify '. 'multiple devices or users.')); } else if (!$public_key->getIsTrusted()) { throw new PhutilArgumentUsageException( pht( 'The public key corresponding to the given private key is '. 'already associated with the device, but is not trusted. '. 'Registering this key would trust the other entities which '. 'hold it. Use a unique key, or explicitly enable trust for the '. 'current key.')); } else if (!$args->getArg('allow-key-reuse')) { throw new PhutilArgumentUsageException( pht( 'The public key corresponding to the given private key is '. 'already associated with the device. If you do not want to '. 'use a unique key, use --allow-key-reuse to permit '. 'reassociation.')); } } else { $public_key = id(new PhabricatorAuthSSHKey()) ->setObjectPHID($device->getPHID()) ->attachObject($device) ->setName($device->getSSHKeyDefaultName()) ->setKeyType($key_object->getType()) ->setKeyBody($key_object->getBody()) ->setKeyComment(pht('Registered')) ->setIsTrusted(1); } $console->writeOut( "%s\n", pht('Installing public key...')); $tmp_public = new TempFile(); Filesystem::changePermissions($tmp_public, 0600); execx('chown %s %s', $phd_user, $tmp_public); Filesystem::writeFile($tmp_public, $raw_public_key); execx('mv -f %s %s', $tmp_public, $stored_public_path); $console->writeOut( "%s\n", pht('Installing private key...')); execx('mv -f %s %s', $tmp_private, $stored_private_path); $raw_device = $device_name; $identify_as = $args->getArg('identify-as'); if (strlen($identify_as)) { $raw_device = $identify_as; } $console->writeOut( "%s\n", - pht('Installing device ID...', $raw_device)); + pht('Installing device %s...', $raw_device)); // The permissions on this file are more open because the webserver also // needs to read it. $tmp_device = new TempFile(); Filesystem::changePermissions($tmp_device, 0644); execx('chown %s %s', $phd_user, $tmp_device); Filesystem::writeFile($tmp_device, $raw_device); execx('mv -f %s %s', $tmp_device, $stored_device_path); if (!$public_key->getID()) { $console->writeOut( "%s\n", pht('Registering device key...')); $public_key->save(); } $console->writeOut( "** %s ** %s\n", pht('HOST REGISTERED'), pht( 'This host has been registered as "%s" and a trusted keypair '. 'has been installed.', $raw_device)); } } diff --git a/src/applications/conduit/protocol/exception/ConduitMethodDoesNotExistException.php b/src/applications/conduit/protocol/exception/ConduitMethodDoesNotExistException.php index 19311df9e1..27c361e05d 100644 --- a/src/applications/conduit/protocol/exception/ConduitMethodDoesNotExistException.php +++ b/src/applications/conduit/protocol/exception/ConduitMethodDoesNotExistException.php @@ -1,12 +1,13 @@ issue = $issue; return $this; } public function getIssue() { return $this->issue; } public function render() { $issue = $this->getIssue(); $description = array(); $description[] = phutil_tag( 'div', array( 'class' => 'setup-issue-instructions', ), phutil_escape_html_newlines($issue->getMessage())); $configs = $issue->getPHPConfig(); if ($configs) { $description[] = $this->renderPHPConfig($configs, $issue); } $configs = $issue->getMySQLConfig(); if ($configs) { $description[] = $this->renderMySQLConfig($configs); } $configs = $issue->getPhabricatorConfig(); if ($configs) { $description[] = $this->renderPhabricatorConfig($configs); } $related_configs = $issue->getRelatedPhabricatorConfig(); if ($related_configs) { $description[] = $this->renderPhabricatorConfig($related_configs, $related = true); } $commands = $issue->getCommands(); if ($commands) { $run_these = pht('Run these %d command(s):', count($commands)); $description[] = phutil_tag( 'div', array( 'class' => 'setup-issue-config', ), array( phutil_tag('p', array(), $run_these), phutil_tag('pre', array(), phutil_implode_html("\n", $commands)), )); } $extensions = $issue->getPHPExtensions(); if ($extensions) { $install_these = pht( 'Install these %d PHP extension(s):', count($extensions)); $install_info = pht( 'You can usually install a PHP extension using %s or %s. Common '. 'package names are %s or %s. Try commands like these:', phutil_tag('tt', array(), 'apt-get'), phutil_tag('tt', array(), 'yum'), hsprintf('php-%s', pht('extname')), hsprintf('php5-%s', pht('extname'))); // TODO: We should do a better job of detecting how to install extensions // on the current system. $install_commands = hsprintf( "\$ sudo apt-get install php5-extname ". "# Debian / Ubuntu\n". "\$ sudo yum install php-extname ". "# Red Hat / Derivatives"); $fallback_info = pht( "If those commands don't work, try Google. The process of installing ". "PHP extensions is not specific to Phabricator, and any instructions ". "you can find for installing them on your system should work. On Mac ". "OS X, you might want to try Homebrew."); $restart_info = pht( 'After installing new PHP extensions, restart your webserver '. 'for the changes to take effect.', hsprintf('')); $description[] = phutil_tag( 'div', array( 'class' => 'setup-issue-config', ), array( phutil_tag('p', array(), $install_these), phutil_tag('pre', array(), implode("\n", $extensions)), phutil_tag('p', array(), $install_info), phutil_tag('pre', array(), $install_commands), phutil_tag('p', array(), $fallback_info), phutil_tag('p', array(), $restart_info), )); } $related_links = $issue->getLinks(); if ($related_links) { $description[] = $this->renderRelatedLinks($related_links); } $actions = array(); if (!$issue->getIsFatal()) { if ($issue->getIsIgnored()) { $actions[] = javelin_tag( 'a', array( 'href' => '/config/unignore/'.$issue->getIssueKey().'/', 'sigil' => 'workflow', 'class' => 'button grey', ), pht('Unignore Setup Issue')); } else { $actions[] = javelin_tag( 'a', array( 'href' => '/config/ignore/'.$issue->getIssueKey().'/', 'sigil' => 'workflow', 'class' => 'button grey', ), pht('Ignore Setup Issue')); } $actions[] = javelin_tag( 'a', array( 'href' => '/config/issue/'.$issue->getIssueKey().'/', 'class' => 'button grey', 'style' => 'float: right', ), pht('Reload Page')); } if ($actions) { $actions = phutil_tag( 'div', array( 'class' => 'setup-issue-actions', ), $actions); } if ($issue->getIsIgnored()) { $status = phutil_tag( 'div', array( 'class' => 'setup-issue-status', ), pht( 'This issue is currently ignored, and does not show a global '. 'warning.')); $next = null; } else { $status = null; $next = phutil_tag( 'div', array( 'class' => 'setup-issue-next', ), pht('To continue, resolve this problem and reload the page.')); } $name = phutil_tag( 'div', array( 'class' => 'setup-issue-name', ), $issue->getName()); $head = phutil_tag( 'div', array( 'class' => 'setup-issue-head', ), array($name, $status)); $tail = phutil_tag( 'div', array( 'class' => 'setup-issue-tail', ), array($actions, $next)); $issue = phutil_tag( 'div', array( 'class' => 'setup-issue', ), array( $head, $description, $tail, )); $debug_info = phutil_tag( 'div', array( 'class' => 'setup-issue-debug', ), pht('Host: %s', php_uname('n'))); return phutil_tag( 'div', array( 'class' => 'setup-issue-shell', ), array( $issue, $debug_info, )); } private function renderPhabricatorConfig(array $configs, $related = false) { $issue = $this->getIssue(); $table_info = phutil_tag( 'p', array(), pht( 'The current Phabricator configuration has these %d value(s):', count($configs))); $options = PhabricatorApplicationConfigOptions::loadAllOptions(); $hidden = array(); foreach ($options as $key => $option) { if ($option->getHidden()) { $hidden[$key] = true; } } $table = null; $dict = array(); foreach ($configs as $key) { if (isset($hidden[$key])) { $dict[$key] = null; } else { $dict[$key] = PhabricatorEnv::getUnrepairedEnvConfig($key); } } $table = $this->renderValueTable($dict, $hidden); if ($this->getIssue()->getIsFatal()) { $update_info = phutil_tag( 'p', array(), pht( 'To update these %d value(s), run these command(s) from the command '. 'line:', count($configs))); $update = array(); foreach ($configs as $key) { $update[] = hsprintf( 'phabricator/ $ ./bin/config set %s value', $key); } $update = phutil_tag('pre', array(), phutil_implode_html("\n", $update)); } else { $update = array(); foreach ($configs as $config) { if (idx($options, $config) && $options[$config]->getLocked()) { continue; } $link = phutil_tag( 'a', array( 'href' => '/config/edit/'.$config.'/?issue='.$issue->getIssueKey(), ), pht('Edit %s', $config)); $update[] = phutil_tag('li', array(), $link); } if ($update) { $update = phutil_tag('ul', array(), $update); if (!$related) { $update_info = phutil_tag( 'p', array(), pht('You can update these %d value(s) here:', count($configs))); } else { $update_info = phutil_tag( 'p', array(), pht('These %d configuration value(s) are related:', count($configs))); } } else { $update = null; $update_info = null; } } return phutil_tag( 'div', array( 'class' => 'setup-issue-config', ), array( $table_info, $table, $update_info, $update, )); } private function renderPHPConfig(array $configs, $issue) { $table_info = phutil_tag( 'p', array(), pht( 'The current PHP configuration has these %d value(s):', count($configs))); $dict = array(); foreach ($configs as $key) { $dict[$key] = $issue->getPHPConfigOriginalValue( $key, ini_get($key)); } $table = $this->renderValueTable($dict); ob_start(); phpinfo(); $phpinfo = ob_get_clean(); $rex = '@Loaded Configuration File\s*(.*?)@i'; $matches = null; $ini_loc = null; if (preg_match($rex, $phpinfo, $matches)) { $ini_loc = trim($matches[1]); } $rex = '@Additional \.ini files parsed\s*(.*?)@i'; $more_loc = array(); if (preg_match($rex, $phpinfo, $matches)) { $more_loc = trim($matches[1]); if ($more_loc == '(none)') { $more_loc = array(); } else { $more_loc = preg_split('/\s*,\s*/', $more_loc); } } $info = array(); if (!$ini_loc) { $info[] = phutil_tag( 'p', array(), pht( 'To update these %d value(s), edit your PHP configuration file.', count($configs))); } else { $info[] = phutil_tag( 'p', array(), pht( 'To update these %d value(s), edit your PHP configuration file, '. 'located here:', count($configs))); $info[] = phutil_tag( 'pre', array(), $ini_loc); } if ($more_loc) { $info[] = phutil_tag( 'p', array(), pht( - 'PHP also loaded these configuration file(s):', - count($more_loc))); + 'PHP also loaded these %s configuration file(s):', + new PhutilNumber(count($more_loc)))); $info[] = phutil_tag( 'pre', array(), implode("\n", $more_loc)); } $info[] = phutil_tag( 'p', array(), pht( 'You can find more information about PHP configuration values in the '. '%s.', phutil_tag( 'a', array( 'href' => 'http://php.net/manual/ini.list.php', 'target' => '_blank', ), pht('PHP Documentation')))); $info[] = phutil_tag( 'p', array(), pht( 'After editing the PHP configuration, restart your '. 'webserver for the changes to take effect.', hsprintf(''))); return phutil_tag( 'div', array( 'class' => 'setup-issue-config', ), array( $table_info, $table, $info, )); } private function renderMySQLConfig(array $config) { $values = array(); foreach ($config as $key) { $value = PhabricatorMySQLSetupCheck::loadRawConfigValue($key); if ($value === null) { $value = phutil_tag( 'em', array(), pht('(Not Supported)')); } $values[$key] = $value; } $table = $this->renderValueTable($values); $doc_href = PhabricatorEnv::getDoclink('User Guide: Amazon RDS'); $doc_link = phutil_tag( 'a', array( 'href' => $doc_href, 'target' => '_blank', ), pht('User Guide: Amazon RDS')); $info = array(); $info[] = phutil_tag( 'p', array(), pht( 'If you are using Amazon RDS, some of the instructions above may '. 'not apply to you. See %s for discussion of Amazon RDS.', $doc_link)); $table_info = phutil_tag( 'p', array(), pht( 'The current MySQL configuration has these %d value(s):', count($config))); return phutil_tag( 'div', array( 'class' => 'setup-issue-config', ), array( $table_info, $table, $info, )); } private function renderValueTable(array $dict, array $hidden = array()) { $rows = array(); foreach ($dict as $key => $value) { if (isset($hidden[$key])) { $value = phutil_tag('em', array(), 'hidden'); } else { $value = $this->renderValueForDisplay($value); } $cols = array( phutil_tag('th', array(), $key), phutil_tag('td', array(), $value), ); $rows[] = phutil_tag('tr', array(), $cols); } return phutil_tag('table', array(), $rows); } private function renderValueForDisplay($value) { if ($value === null) { return phutil_tag('em', array(), 'null'); } else if ($value === false) { return phutil_tag('em', array(), 'false'); } else if ($value === true) { return phutil_tag('em', array(), 'true'); } else if ($value === '') { return phutil_tag('em', array(), 'empty string'); } else if ($value instanceof PhutilSafeHTML) { return $value; } else { return PhabricatorConfigJSON::prettyPrintJSON($value); } } private function renderRelatedLinks(array $links) { $link_info = phutil_tag( 'p', array(), pht( '%d related link(s):', count($links))); $link_list = array(); foreach ($links as $link) { $link_tag = phutil_tag( 'a', array( 'target' => '_blank', 'href' => $link['href'], ), $link['name']); $link_item = phutil_tag('li', array(), $link_tag); $link_list[] = $link_item; } $link_list = phutil_tag('ul', array(), $link_list); return phutil_tag( 'div', array( 'class' => 'setup-issue-config', ), array( $link_info, $link_list, )); } } diff --git a/src/applications/differential/event/DifferentialHovercardEventListener.php b/src/applications/differential/event/DifferentialHovercardEventListener.php index 80ef4eb249..82ec5565c1 100644 --- a/src/applications/differential/event/DifferentialHovercardEventListener.php +++ b/src/applications/differential/event/DifferentialHovercardEventListener.php @@ -1,80 +1,80 @@ listen(PhabricatorEventType::TYPE_UI_DIDRENDERHOVERCARD); } public function handleEvent(PhutilEvent $event) { switch ($event->getType()) { case PhabricatorEventType::TYPE_UI_DIDRENDERHOVERCARD: $this->handleHovercardEvent($event); break; } } private function handleHovercardEvent($event) { $viewer = $event->getUser(); $hovercard = $event->getValue('hovercard'); $object_handle = $event->getValue('handle'); $phid = $object_handle->getPHID(); $rev = $event->getValue('object'); if (!($rev instanceof DifferentialRevision)) { return; } $rev->loadRelationships(); $reviewer_phids = $rev->getReviewers(); $e_task = DifferentialRevisionHasTaskEdgeType::EDGECONST; $edge_query = id(new PhabricatorEdgeQuery()) ->withSourcePHIDs(array($phid)) ->withEdgeTypes( array( $e_task, )); $edge_query->execute(); $tasks = $edge_query->getDestinationPHIDs(); $phids = array_merge( array( $rev->getAuthorPHID(), ), $reviewer_phids, $tasks); $handles = id(new PhabricatorHandleQuery()) ->setViewer($viewer) ->withPHIDs($phids) ->execute(); $hovercard->setTitle('D'.$rev->getID()); $hovercard->setDetail($rev->getTitle()); $hovercard->addField(pht('Author'), $handles[$rev->getAuthorPHID()]->renderLink()); $hovercard->addField(pht('Reviewers'), implode_selected_handle_links(', ', $handles, $reviewer_phids)); if ($tasks) { - $hovercard->addField(pht('Task(s)', count($tasks)), + $hovercard->addField(pht('%s Task(s)', new PhutilNumber(count($tasks))), implode_selected_handle_links(', ', $handles, $tasks)); } if ($rev->getSummary()) { $hovercard->addField(pht('Summary'), id(new PhutilUTF8StringTruncator()) ->setMaximumGlyphs(120) ->truncateString($rev->getSummary())); } $hovercard->addTag( DifferentialRevisionDetailView::renderTagForRevision($rev)); $event->setValue('hovercard', $hovercard); } } diff --git a/src/applications/differential/mail/DifferentialCreateMailReceiver.php b/src/applications/differential/mail/DifferentialCreateMailReceiver.php index 9d9ac05043..614c52306d 100644 --- a/src/applications/differential/mail/DifferentialCreateMailReceiver.php +++ b/src/applications/differential/mail/DifferentialCreateMailReceiver.php @@ -1,123 +1,123 @@ canAcceptApplicationMail($differential_app, $mail); } protected function processReceivedMail( PhabricatorMetaMTAReceivedMail $mail, PhabricatorUser $sender) { $attachments = $mail->getAttachments(); $files = array(); $errors = array(); if ($attachments) { $files = id(new PhabricatorFileQuery()) ->setViewer($sender) ->withPHIDs($attachments) ->execute(); foreach ($files as $index => $file) { if ($file->getMimeType() != 'text/plain') { $errors[] = pht( 'Could not parse file %s; only files with mimetype text/plain '. 'can be parsed via email.', $file->getName()); unset($files[$index]); } } } $diffs = array(); foreach ($files as $file) { $call = new ConduitCall( 'differential.createrawdiff', array( 'diff' => $file->loadFileData(), )); $call->setUser($sender); try { $result = $call->execute(); $diffs[$file->getName()] = $result['uri']; } catch (Exception $e) { $errors[] = pht( 'Could not parse attachment %s; only attachments (and mail bodies) '. 'generated via "diff" commands can be parsed.', $file->getName()); } } $body = $mail->getCleanTextBody(); if ($body) { $call = new ConduitCall( 'differential.createrawdiff', array( 'diff' => $body,)); $call->setUser($sender); try { $result = $call->execute(); $diffs[pht('Mail Body')] = $result['uri']; } catch (Exception $e) { $errors[] = pht( 'Could not parse mail body; only mail bodies (and attachments) '. 'generated via "diff" commands can be parsed.'); } } $subject_prefix = PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix'); if (count($diffs)) { $subject = pht( 'You successfully created %d diff(s).', count($diffs)); } else { $subject = pht( - 'Diff creation failed; see body for error(s).', - count($errors)); + 'Diff creation failed; see body for %s error(s).', + new PhutilNumber(count($errors))); } $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection($subject); if (count($diffs)) { $text_body = ''; $html_body = array(); - $body_label = pht('DIFF LINK(S)', count($diffs)); + $body_label = pht('%s DIFF LINK(S)', new PhutilNumber(count($diffs))); foreach ($diffs as $filename => $diff_uri) { $text_body .= $filename.': '.$diff_uri."\n"; $html_body[] = phutil_tag( 'a', array( 'href' => $diff_uri, ), $filename); $html_body[] = phutil_tag('br'); } $body->addTextSection($body_label, $text_body); $body->addHTMLSection($body_label, $html_body); } if (count($errors)) { $body_section = new PhabricatorMetaMTAMailSection(); - $body_label = pht('ERROR(S)', count($errors)); + $body_label = pht('%s ERROR(S)', new PhutilNumber(count($errors))); foreach ($errors as $error) { $body_section->addFragment($error); } $body->addTextSection($body_label, $body_section); } id(new PhabricatorMetaMTAMail()) ->addTos(array($sender->getPHID())) ->setSubject($subject) ->setSubjectPrefix($subject_prefix) ->setFrom($sender->getPHID()) ->setBody($body->render()) ->saveAndSend(); } } diff --git a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php index f3790e4b7c..dc25eb8950 100644 --- a/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php +++ b/src/applications/differential/render/DifferentialChangesetHTMLRenderer.php @@ -1,459 +1,434 @@ getChangeset(); $change = $changeset->getChangeType(); $file = $changeset->getFileType(); $messages = array(); - $none = hsprintf(''); switch ($change) { case DifferentialChangeType::TYPE_ADD: switch ($file) { case DifferentialChangeType::FILE_TEXT: - $messages[] = pht( - 'This file was added.', - $none); + $messages[] = pht('This file was added.'); break; case DifferentialChangeType::FILE_IMAGE: - $messages[] = pht( - 'This image was added.', - $none); + $messages[] = pht('This image was added.'); break; case DifferentialChangeType::FILE_DIRECTORY: - $messages[] = pht( - 'This directory was added.', - $none); + $messages[] = pht('This directory was added.'); break; case DifferentialChangeType::FILE_BINARY: - $messages[] = pht( - 'This binary file was added.', - $none); + $messages[] = pht('This binary file was added.'); break; case DifferentialChangeType::FILE_SYMLINK: - $messages[] = pht( - 'This symlink was added.', - $none); + $messages[] = pht('This symlink was added.'); break; case DifferentialChangeType::FILE_SUBMODULE: - $messages[] = pht( - 'This submodule was added.', - $none); + $messages[] = pht('This submodule was added.'); break; } break; case DifferentialChangeType::TYPE_DELETE: switch ($file) { case DifferentialChangeType::FILE_TEXT: - $messages[] = pht( - 'This file was deleted.', - $none); + $messages[] = pht('This file was deleted.'); break; case DifferentialChangeType::FILE_IMAGE: - $messages[] = pht( - 'This image was deleted.', - $none); + $messages[] = pht('This image was deleted.'); break; case DifferentialChangeType::FILE_DIRECTORY: - $messages[] = pht( - 'This directory was deleted.', - $none); + $messages[] = pht('This directory was deleted.'); break; case DifferentialChangeType::FILE_BINARY: - $messages[] = pht( - 'This binary file was deleted.', - $none); + $messages[] = pht('This binary file was deleted.'); break; case DifferentialChangeType::FILE_SYMLINK: - $messages[] = pht( - 'This symlink was deleted.', - $none); + $messages[] = pht('This symlink was deleted.'); break; case DifferentialChangeType::FILE_SUBMODULE: - $messages[] = pht( - 'This submodule was deleted.', - $none); + $messages[] = pht('This submodule was deleted.'); break; } break; case DifferentialChangeType::TYPE_MOVE_HERE: $from = phutil_tag('strong', array(), $changeset->getOldFile()); switch ($file) { case DifferentialChangeType::FILE_TEXT: $messages[] = pht('This file was moved from %s.', $from); break; case DifferentialChangeType::FILE_IMAGE: $messages[] = pht('This image was moved from %s.', $from); break; case DifferentialChangeType::FILE_DIRECTORY: $messages[] = pht('This directory was moved from %s.', $from); break; case DifferentialChangeType::FILE_BINARY: $messages[] = pht('This binary file was moved from %s.', $from); break; case DifferentialChangeType::FILE_SYMLINK: $messages[] = pht('This symlink was moved from %s.', $from); break; case DifferentialChangeType::FILE_SUBMODULE: $messages[] = pht('This submodule was moved from %s.', $from); break; } break; case DifferentialChangeType::TYPE_COPY_HERE: $from = phutil_tag('strong', array(), $changeset->getOldFile()); switch ($file) { case DifferentialChangeType::FILE_TEXT: $messages[] = pht('This file was copied from %s.', $from); break; case DifferentialChangeType::FILE_IMAGE: $messages[] = pht('This image was copied from %s.', $from); break; case DifferentialChangeType::FILE_DIRECTORY: $messages[] = pht('This directory was copied from %s.', $from); break; case DifferentialChangeType::FILE_BINARY: $messages[] = pht('This binary file was copied from %s.', $from); break; case DifferentialChangeType::FILE_SYMLINK: $messages[] = pht('This symlink was copied from %s.', $from); break; case DifferentialChangeType::FILE_SUBMODULE: $messages[] = pht('This submodule was copied from %s.', $from); break; } break; case DifferentialChangeType::TYPE_MOVE_AWAY: $paths = phutil_tag( 'strong', array(), implode(', ', $changeset->getAwayPaths())); switch ($file) { case DifferentialChangeType::FILE_TEXT: $messages[] = pht('This file was moved to %s.', $paths); break; case DifferentialChangeType::FILE_IMAGE: $messages[] = pht('This image was moved to %s.', $paths); break; case DifferentialChangeType::FILE_DIRECTORY: $messages[] = pht('This directory was moved to %s.', $paths); break; case DifferentialChangeType::FILE_BINARY: $messages[] = pht('This binary file was moved to %s.', $paths); break; case DifferentialChangeType::FILE_SYMLINK: $messages[] = pht('This symlink was moved to %s.', $paths); break; case DifferentialChangeType::FILE_SUBMODULE: $messages[] = pht('This submodule was moved to %s.', $paths); break; } break; case DifferentialChangeType::TYPE_COPY_AWAY: $paths = phutil_tag( 'strong', array(), implode(', ', $changeset->getAwayPaths())); switch ($file) { case DifferentialChangeType::FILE_TEXT: $messages[] = pht('This file was copied to %s.', $paths); break; case DifferentialChangeType::FILE_IMAGE: $messages[] = pht('This image was copied to %s.', $paths); break; case DifferentialChangeType::FILE_DIRECTORY: $messages[] = pht('This directory was copied to %s.', $paths); break; case DifferentialChangeType::FILE_BINARY: $messages[] = pht('This binary file was copied to %s.', $paths); break; case DifferentialChangeType::FILE_SYMLINK: $messages[] = pht('This symlink was copied to %s.', $paths); break; case DifferentialChangeType::FILE_SUBMODULE: $messages[] = pht('This submodule was copied to %s.', $paths); break; } break; case DifferentialChangeType::TYPE_MULTICOPY: $paths = phutil_tag( 'strong', array(), implode(', ', $changeset->getAwayPaths())); switch ($file) { case DifferentialChangeType::FILE_TEXT: $messages[] = pht( 'This file was deleted after being copied to %s.', $paths); break; case DifferentialChangeType::FILE_IMAGE: $messages[] = pht( 'This image was deleted after being copied to %s.', $paths); break; case DifferentialChangeType::FILE_DIRECTORY: $messages[] = pht( 'This directory was deleted after being copied to %s.', $paths); break; case DifferentialChangeType::FILE_BINARY: $messages[] = pht( 'This binary file was deleted after being copied to %s.', $paths); break; case DifferentialChangeType::FILE_SYMLINK: $messages[] = pht( 'This symlink was deleted after being copied to %s.', $paths); break; case DifferentialChangeType::FILE_SUBMODULE: $messages[] = pht( 'This submodule was deleted after being copied to %s.', $paths); break; } break; default: switch ($file) { case DifferentialChangeType::FILE_TEXT: // This is the default case, so we only render this header if // forced to since it's not very useful. if ($force) { $messages[] = pht('This file was not modified.'); } break; case DifferentialChangeType::FILE_IMAGE: $messages[] = pht('This is an image.'); break; case DifferentialChangeType::FILE_DIRECTORY: $messages[] = pht('This is a directory.'); break; case DifferentialChangeType::FILE_BINARY: $messages[] = pht('This is a binary file.'); break; case DifferentialChangeType::FILE_SYMLINK: $messages[] = pht('This is a symlink.'); break; case DifferentialChangeType::FILE_SUBMODULE: $messages[] = pht('This is a submodule.'); break; } break; } // If this is a text file with at least one hunk, we may have converted // the text encoding. In this case, show a note. $show_encoding = ($file == DifferentialChangeType::FILE_TEXT) && ($changeset->getHunks()); if ($show_encoding) { $encoding = $this->getOriginalCharacterEncoding(); if ($encoding != 'utf8') { if ($encoding) { $messages[] = pht( 'This file was converted from %s for display.', phutil_tag('strong', array(), $encoding)); } else { $messages[] = pht( 'This file uses an unknown character encoding.'); } } } if (!$messages) { return null; } foreach ($messages as $key => $message) { $messages[$key] = phutil_tag('li', array(), $message); } return phutil_tag( 'ul', array( 'class' => 'differential-meta-notice', ), $messages); } protected function renderPropertyChangeHeader() { $changeset = $this->getChangeset(); list($old, $new) = $this->getChangesetProperties($changeset); // If we don't have any property changes, don't render this table. if ($old === $new) { return null; } $keys = array_keys($old + $new); sort($keys); $key_map = array( 'unix:filemode' => pht('File Mode'), 'file:dimensions' => pht('Image Dimensions'), 'file:mimetype' => pht('MIME Type'), 'file:size' => pht('File Size'), ); $rows = array(); foreach ($keys as $key) { $oval = idx($old, $key); $nval = idx($new, $key); if ($oval !== $nval) { if ($oval === null) { $oval = phutil_tag('em', array(), 'null'); } else { $oval = phutil_escape_html_newlines($oval); } if ($nval === null) { $nval = phutil_tag('em', array(), 'null'); } else { $nval = phutil_escape_html_newlines($nval); } $readable_key = idx($key_map, $key, $key); $row = array( $readable_key, $oval, $nval, ); $rows[] = $row; } } $classes = array('', 'oval', 'nval'); $headers = array( pht('Property'), pht('Old Value'), pht('New Value'), ); $table = id(new AphrontTableView($rows)) ->setHeaders($headers) ->setColumnClasses($classes); return phutil_tag( 'div', array( 'class' => 'differential-property-table', ), $table); } public function renderShield($message, $force = 'default') { $end = count($this->getOldLines()); $reference = $this->getRenderingReference(); if ($force !== 'text' && $force !== 'whitespace' && $force !== 'none' && $force !== 'default') { throw new Exception("Invalid 'force' parameter '{$force}'!"); } $range = "0-{$end}"; if ($force == 'text') { // If we're forcing text, force the whole file to be rendered. $range = "{$range}/0-{$end}"; } $meta = array( 'ref' => $reference, 'range' => $range, ); if ($force == 'whitespace') { $meta['whitespace'] = DifferentialChangesetParser::WHITESPACE_SHOW_ALL; } $content = array(); $content[] = $message; if ($force !== 'none') { $content[] = ' '; $content[] = javelin_tag( 'a', array( 'mustcapture' => true, 'sigil' => 'show-more', 'class' => 'complete', 'href' => '#', 'meta' => $meta, ), pht('Show File Contents')); } return $this->wrapChangeInTable( javelin_tag( 'tr', array( 'sigil' => 'context-target', ), phutil_tag( 'td', array( 'class' => 'differential-shield', 'colspan' => 6, ), $content))); } abstract protected function renderColgroup(); protected function wrapChangeInTable($content) { if (!$content) { return null; } return javelin_tag( 'table', array( 'class' => 'differential-diff remarkup-code PhabricatorMonospaced', 'sigil' => 'differential-diff', ), array( $this->renderColgroup(), $content, )); } protected function renderInlineComment( PhabricatorInlineCommentInterface $comment, $on_right = false) { return $this->buildInlineComment($comment, $on_right)->render(); } protected function buildInlineComment( PhabricatorInlineCommentInterface $comment, $on_right = false) { $user = $this->getUser(); $edit = $user && ($comment->getAuthorPHID() == $user->getPHID()) && ($comment->isDraft()) && $this->getShowEditAndReplyLinks(); $allow_reply = (bool)$user && $this->getShowEditAndReplyLinks(); return id(new DifferentialInlineCommentView()) ->setInlineComment($comment) ->setOnRight($on_right) ->setHandles($this->getHandles()) ->setMarkupEngine($this->getMarkupEngine()) ->setEditable($edit) ->setAllowReply($allow_reply); } } diff --git a/src/applications/people/controller/PhabricatorPeopleApproveController.php b/src/applications/people/controller/PhabricatorPeopleApproveController.php index ce1ac85dde..bbf69f23f8 100644 --- a/src/applications/people/controller/PhabricatorPeopleApproveController.php +++ b/src/applications/people/controller/PhabricatorPeopleApproveController.php @@ -1,67 +1,66 @@ id = idx($data, 'id'); } public function processRequest() { $request = $this->getRequest(); $admin = $request->getUser(); $user = id(new PhabricatorPeopleQuery()) ->setViewer($admin) ->withIDs(array($this->id)) ->executeOne(); if (!$user) { return new Aphront404Response(); } $done_uri = $this->getApplicationURI('query/approval/'); if ($request->isFormPost()) { id(new PhabricatorUserEditor()) ->setActor($admin) ->approveUser($user, true); $title = pht( 'Phabricator Account "%s" Approved', - $user->getUsername(), - $admin->getUsername()); + $user->getUsername()); $body = pht( "Your Phabricator account (%s) has been approved by %s. You can ". "login here:\n\n %s\n\n", $user->getUsername(), $admin->getUsername(), PhabricatorEnv::getProductionURI('/')); $mail = id(new PhabricatorMetaMTAMail()) ->addTos(array($user->getPHID())) ->addCCs(array($admin->getPHID())) ->setSubject('[Phabricator] '.$title) ->setForceDelivery(true) ->setBody($body) ->saveAndSend(); return id(new AphrontRedirectResponse())->setURI($done_uri); } $dialog = id(new AphrontDialogView()) ->setUser($admin) ->setTitle(pht('Confirm Approval')) ->appendChild( pht( 'Allow %s to access this Phabricator install?', phutil_tag('strong', array(), $user->getUsername()))) ->addCancelButton($done_uri) ->addSubmitButton(pht('Approve Account')); return id(new AphrontDialogResponse())->setDialog($dialog); } } diff --git a/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php b/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php index bcfd6b061c..3a9508e197 100644 --- a/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php +++ b/src/applications/repository/data/PhabricatorRepositoryURINormalizer.php @@ -1,123 +1,123 @@ getNormalizedPath() == $norm_b->getNormalizedPath()) { * // URIs appear to point at the same repository. * } else { * // URIs are very unlikely to be the same repository. * } * * Because a repository can be hosted at arbitrarly many arbitrary URIs, there * is no way to completely prevent false negatives by only examining URIs * (that is, repositories with totally different URIs could really be the same). * However, normalization is relatively agressive and false negatives should * be rare: if normalization says two URIs are different repositories, they * probably are. * * @task normal Normalizing URIs */ final class PhabricatorRepositoryURINormalizer extends Phobject { const TYPE_GIT = 'git'; const TYPE_SVN = 'svn'; const TYPE_MERCURIAL = 'hg'; private $type; private $uri; public function __construct($type, $uri) { switch ($type) { case self::TYPE_GIT: case self::TYPE_SVN: case self::TYPE_MERCURIAL: break; default: - throw new Exception(pht('Unknown URI type "%s"!')); + throw new Exception(pht('Unknown URI type "%s"!', $type)); } $this->type = $type; $this->uri = $uri; } /* -( Normalizing URIs )--------------------------------------------------- */ /** * @task normal */ public function getPath() { switch ($this->type) { case self::TYPE_GIT: $uri = new PhutilURI($this->uri); if ($uri->getProtocol()) { return $uri->getPath(); } $uri = new PhutilGitURI($this->uri); if ($uri->getDomain()) { return $uri->getPath(); } return $this->uri; case self::TYPE_SVN: case self::TYPE_MERCURIAL: $uri = new PhutilURI($this->uri); if ($uri->getProtocol()) { return $uri->getPath(); } return $this->uri; } } /** * @task normal */ public function getNormalizedPath() { $path = $this->getPath(); $path = trim($path, '/'); switch ($this->type) { case self::TYPE_GIT: $path = preg_replace('/\.git$/', '', $path); break; case self::TYPE_SVN: case self::TYPE_MERCURIAL: break; } // If this is a Phabricator URI, strip it down to the callsign. We mutably // allow you to clone repositories as "/diffusion/X/anything.git", for // example. $matches = null; if (preg_match('@^(diffusion/[A-Z]+)@', $path, $matches)) { $path = $matches[1]; } return $path; } } diff --git a/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php b/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php index 1f93618042..e4f03a4d1b 100644 --- a/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php +++ b/src/applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php @@ -1,112 +1,114 @@ setName('lookup-users') ->setExamples('**lookup-users** __commit__ ...') ->setSynopsis('Resolve user accounts for users attached to __commit__.') ->setArguments( array( array( 'name' => 'commits', 'wildcard' => true, ), )); } public function execute(PhutilArgumentParser $args) { $commits = $this->loadCommits($args, 'commits'); if (!$commits) { throw new PhutilArgumentUsageException( 'Specify one or more commits to resolve users for.'); } $console = PhutilConsole::getConsole(); foreach ($commits as $commit) { $repo = $commit->getRepository(); $name = $repo->formatCommitName($commit->getCommitIdentifier()); $console->writeOut( "%s\n", pht('Examining commit %s...', $name)); $refs_raw = DiffusionQuery::callConduitWithDiffusionRequest( $this->getViewer(), DiffusionRequest::newFromDictionary( array( 'repository' => $repo, 'user' => $this->getViewer(), )), 'diffusion.querycommits', array( 'phids' => array($commit->getPHID()), 'bypassCache' => true, )); if (empty($refs_raw['data'])) { throw new Exception( - pht('Unable to retrieve details for commit "%s"!')); + pht( + 'Unable to retrieve details for commit "%s"!', + $commit->getPHID())); } $ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data'])); $author = $ref->getAuthor(); $console->writeOut( "%s\n", pht('Raw author string: %s', coalesce($author, 'null'))); if ($author !== null) { $handle = $this->resolveUser($commit, $author); if ($handle) { $console->writeOut( "%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut( "%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } $committer = $ref->getCommitter(); $console->writeOut( "%s\n", pht('Raw committer string: %s', coalesce($committer, 'null'))); if ($committer !== null) { $handle = $this->resolveUser($commit, $committer); if ($handle) { $console->writeOut( "%s\n", pht('Phabricator user: %s', $handle->getFullName())); } else { $console->writeOut( "%s\n", pht('Unable to resolve a corresponding Phabricator user.')); } } } return 0; } private function resolveUser(PhabricatorRepositoryCommit $commit, $name) { $phid = id(new DiffusionResolveUserQuery()) ->withCommit($commit) ->withName($name) ->execute(); if (!$phid) { return null; } return id(new PhabricatorHandleQuery()) ->setViewer($this->getViewer()) ->withPHIDs(array($phid)) ->executeOne(); } } diff --git a/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php index 868159642c..3e7355ef8b 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorBaseEnglishTranslation.php @@ -1,930 +1,930 @@ array( 'No daemon with id %s exists!', 'No daemons with ids %s exist!', ), 'These %d configuration value(s) are related:' => array( 'This configuration value is related:', 'These configuration values are related:', ), - 'Task(s)' => array('Task', 'Tasks'), + '%s Task(s)' => array('Task', 'Tasks'), - 'ERROR(S)' => array('ERROR', 'ERRORS'), + '%s ERROR(S)' => array('ERROR', 'ERRORS'), '%d Error(s)' => array('%d Error', '%d Errors'), '%d Warning(s)' => array('%d Warning', '%d Warnings'), '%d Auto-Fix(es)' => array('%d Auto-Fix', '%d Auto-Fixes'), '%d Advice(s)' => array('%d Advice', '%d Pieces of Advice'), '%d Detail(s)' => array('%d Detail', '%d Details'), '(%d line(s))' => array('(%d line)', '(%d lines)'), '%d line(s)' => array('%d line', '%d lines'), '%d path(s)' => array('%d path', '%d paths'), '%d diff(s)' => array('%d diff', '%d diffs'), - 'DIFF LINK(S)' => array('DIFF LINK', 'DIFF LINKS'), + '%s DIFF LINK(S)' => array('DIFF LINK', 'DIFF LINKS'), 'You successfully created %d diff(s).' => array( 'You successfully created %d diff.', 'You successfully created %d diffs.', ), - 'Diff creation failed; see body for error(s).' => array( + 'Diff creation failed; see body for %s error(s).' => array( 'Diff creation failed; see body for error.', 'Diff creation failed; see body for errors.', ), 'There are %d raw fact(s) in storage.' => array( 'There is %d raw fact in storage.', 'There are %d raw facts in storage.', ), 'There are %d aggregate fact(s) in storage.' => array( 'There is %d aggregate fact in storage.', 'There are %d aggregate facts in storage.', ), '%d Commit(s) Awaiting Audit' => array( '%d Commit Awaiting Audit', '%d Commits Awaiting Audit', ), '%d Problem Commit(s)' => array( '%d Problem Commit', '%d Problem Commits', ), '%d Review(s) Blocking Others' => array( '%d Review Blocking Others', '%d Reviews Blocking Others', ), '%d Review(s) Need Attention' => array( '%d Review Needs Attention', '%d Reviews Need Attention', ), '%d Review(s) Waiting on Others' => array( '%d Review Waiting on Others', '%d Reviews Waiting on Others', ), '%d Active Review(s)' => array( '%d Active Review', '%d Active Reviews', ), '%d Flagged Object(s)' => array( '%d Flagged Object', '%d Flagged Objects', ), '%d Object(s) Tracked' => array( '%d Object Tracked', '%d Objects Tracked', ), '%d Assigned Task(s)' => array( '%d Assigned Task', '%d Assigned Tasks', ), 'Show %d Lint Message(s)' => array( 'Show %d Lint Message', 'Show %d Lint Messages', ), 'Hide %d Lint Message(s)' => array( 'Hide %d Lint Message', 'Hide %d Lint Messages', ), 'This is a binary file. It is %s byte(s) in length.' => array( 'This is a binary file. It is %s byte in length.', 'This is a binary file. It is %s bytes in length.', ), '%d Action(s) Have No Effect' => array( 'Action Has No Effect', 'Actions Have No Effect', ), '%d Action(s) With No Effect' => array( 'Action With No Effect', 'Actions With No Effect', ), 'Some of your %d action(s) have no effect:' => array( 'One of your actions has no effect:', 'Some of your actions have no effect:', ), 'Apply remaining %d action(s)?' => array( 'Apply remaining action?', 'Apply remaining actions?', ), 'Apply %d Other Action(s)' => array( 'Apply Remaining Action', 'Apply Remaining Actions', ), 'The %d action(s) you are taking have no effect:' => array( 'The action you are taking has no effect:', 'The actions you are taking have no effect:', ), '%s edited member(s), added %d: %s; removed %d: %s.' => '%s edited members, added: %3$s; removed: %5$s.', '%s added %s member(s): %s.' => array( array( '%s added a member: %3$s.', '%s added members: %3$s.', ), ), '%s removed %s member(s): %s.' => array( array( '%s removed a member: %3$s.', '%s removed members: %3$s.', ), ), '%s edited project(s), added %s: %s; removed %s: %s.' => '%s edited projects, added: %3$s; removed: %5$s.', '%s added %s project(s): %s.' => array( array( '%s added a project: %3$s.', '%s added projects: %3$s.', ), ), '%s removed %s project(s): %s.' => array( array( '%s removed a project: %3$s.', '%s removed projects: %3$s.', ), ), '%s merged %d task(s): %s.' => array( array( '%s merged a task: %3$s.', '%s merged tasks: %3$s.', ), ), '%s merged %d task(s) %s into %s.' => array( array( '%s merged %3$s into %4$s.', '%s merged tasks %3$s into %4$s.', ), ), '%s added %s voting user(s): %s.' => array( array( '%s added a voting user: %3$s.', '%s added voting users: %3$s.', ), ), '%s removed %s voting user(s): %s.' => array( array( '%s removed a voting user: %3$s.', '%s removed voting users: %3$s.', ), ), '%s added %s blocking task(s): %s.' => array( array( '%s added a blocking task: %3$s.', '%s added blocking tasks: %3$s.', ), ), '%s added %s blocked task(s): %s.' => array( array( '%s added a blocked task: %3$s.', '%s added blocked tasks: %3$s.', ), ), '%s removed %s blocking task(s): %s.' => array( array( '%s removed a blocking task: %3$s.', '%s removed blocking tasks: %3$s.', ), ), '%s removed %s blocked task(s): %s.' => array( array( '%s removed a blocked task: %3$s.', '%s removed blocked tasks: %3$s.', ), ), '%s added %s blocking task(s) for %s: %s.' => array( array( '%s added a blocking task for %3$s: %4$s.', '%s added blocking tasks for %3$s: %4$s.', ), ), '%s added %s blocked task(s) for %s: %s.' => array( array( '%s added a blocked task for %3$s: %4$s.', '%s added blocked tasks for %3$s: %4$s.', ), ), '%s removed %s blocking task(s) for %s: %s.' => array( array( '%s removed a blocking task for %3$s: %4$s.', '%s removed blocking tasks for %3$s: %4$s.', ), ), '%s removed %s blocked task(s) for %s: %s.' => array( array( '%s removed a blocked task for %3$s: %4$s.', '%s removed blocked tasks for %3$s: %4$s.', ), ), '%s edited blocking task(s), added %s: %s; removed %s: %s.' => '%s edited blocking tasks, added: %3$s; removed: %5$s.', '%s edited blocking task(s) for %s, added %s: %s; removed %s: %s.' => '%s edited blocking tasks for %s, added: %4$s; removed: %6$s.', '%s edited blocked task(s), added %s: %s; removed %s: %s.' => '%s edited blocked tasks, added: %3$s; removed: %5$s.', '%s edited blocked task(s) for %s, added %s: %s; removed %s: %s.' => '%s edited blocked tasks for %s, added: %4$s; removed: %6$s.', '%s edited answer(s), added %s: %s; removed %d: %s.' => '%s edited answers, added: %3$s; removed: %5$s.', '%s added %s answer(s): %s.' => array( array( '%s added an answer: %3$s.', '%s added answers: %3$s.', ), ), '%s removed %s answer(s): %s.' => array( array( '%s removed a answer: %3$s.', '%s removed answers: %3$s.', ), ), '%s edited question(s), added %s: %s; removed %s: %s.' => '%s edited questions, added: %3$s; removed: %5$s.', '%s added %s question(s): %s.' => array( array( '%s added a question: %3$s.', '%s added questions: %3$s.', ), ), '%s removed %s question(s): %s.' => array( array( '%s removed a question: %3$s.', '%s removed questions: %3$s.', ), ), '%s edited mock(s), added %s: %s; removed %s: %s.' => '%s edited mocks, added: %3$s; removed: %5$s.', '%s added %s mock(s): %s.' => array( array( '%s added a mock: %3$s.', '%s added mocks: %3$s.', ), ), '%s removed %s mock(s): %s.' => array( array( '%s removed a mock: %3$s.', '%s removed mocks: %3$s.', ), ), '%s added %s task(s): %s.' => array( array( '%s added a task: %3$s.', '%s added tasks: %3$s.', ), ), '%s removed %s task(s): %s.' => array( array( '%s removed a task: %3$s.', '%s removed tasks: %3$s.', ), ), '%s edited file(s), added %s: %s; removed %s: %s.' => '%s edited files, added: %3$s; removed: %5$s.', '%s added %s file(s): %s.' => array( array( '%s added a file: %3$s.', '%s added files: %3$s.', ), ), '%s removed %s file(s): %s.' => array( array( '%s removed a file: %3$s.', '%s removed files: %3$s.', ), ), '%s edited contributor(s), added %s: %s; removed %s: %s.' => '%s edited contributors, added: %3$s; removed: %5$s.', '%s added %s contributor(s): %s.' => array( array( '%s added a contributor: %3$s.', '%s added contributors: %3$s.', ), ), '%s removed %s contributor(s): %s.' => array( array( '%s removed a contributor: %3$s.', '%s removed contributors: %3$s.', ), ), '%s edited %s reviewer(s), added %s: %s; removed %s: %s.' => '%s edited reviewers, added: %4$s; removed: %6$s.', '%s edited %s reviewer(s) for %s, added %s: %s; removed %s: %s.' => '%s edited reviewers for %3$s, added: %5$s; removed: %7$s.', '%s added %s reviewer(s): %s.' => array( array( '%s added a reviewer: %3$s.', '%s added reviewers: %3$s.', ), ), '%s removed %s reviewer(s): %s.' => array( array( '%s removed a reviewer: %3$s.', '%s removed reviewers: %3$s.', ), ), '%d other(s)' => array( '1 other', '%d others', ), '%s edited subscriber(s), added %d: %s; removed %d: %s.' => '%s edited subscribers, added: %3$s; removed: %5$s.', '%s added %d subscriber(s): %s.' => array( array( '%s added a subscriber: %3$s.', '%s added subscribers: %3$s.', ), ), '%s removed %d subscriber(s): %s.' => array( array( '%s removed a subscriber: %3$s.', '%s removed subscribers: %3$s.', ), ), '%s edited participant(s), added %d: %s; removed %d: %s.' => '%s edited participants, added: %3$s; removed: %5$s.', '%s added %d participant(s): %s.' => array( array( '%s added a participant: %3$s.', '%s added participants: %3$s.', ), ), '%s removed %d participant(s): %s.' => array( array( '%s removed a participant: %3$s.', '%s removed participants: %3$s.', ), ), '%s edited image(s), added %d: %s; removed %d: %s.' => '%s edited images, added: %3$s; removed: %5$s', '%s added %d image(s): %s.' => array( array( '%s added an image: %3$s.', '%s added images: %3$s.', ), ), '%s removed %d image(s): %s.' => array( array( '%s removed an image: %3$s.', '%s removed images: %3$s.', ), ), '%s Line(s)' => array( '%s Line', '%s Lines', ), 'Indexing %d object(s) of type %s.' => array( 'Indexing %d object of type %s.', 'Indexing %d object of type %s.', ), 'Run these %d command(s):' => array( 'Run this command:', 'Run these commands:', ), 'Install these %d PHP extension(s):' => array( 'Install this PHP extension:', 'Install these PHP extensions:', ), 'The current Phabricator configuration has these %d value(s):' => array( 'The current Phabricator configuration has this value:', 'The current Phabricator configuration has these values:', ), 'The current MySQL configuration has these %d value(s):' => array( 'The current MySQL configuration has this value:', 'The current MySQL configuration has these values:', ), 'You can update these %d value(s) here:' => array( 'You can update this value here:', 'You can update these values here:', ), 'The current PHP configuration has these %d value(s):' => array( 'The current PHP configuration has this value:', 'The current PHP configuration has these values:', ), 'To update these %d value(s), edit your PHP configuration file.' => array( 'To update this %d value, edit your PHP configuration file.', 'To update these %d values, edit your PHP configuration file.', ), 'To update these %d value(s), edit your PHP configuration file, located '. 'here:' => array( 'To update this value, edit your PHP configuration file, located '. 'here:', 'To update these values, edit your PHP configuration file, located '. 'here:', ), - 'PHP also loaded these configuration file(s):' => array( + 'PHP also loaded these %s configuration file(s):' => array( 'PHP also loaded this configuration file:', 'PHP also loaded these configuration files:', ), 'You have %d unresolved setup issue(s)...' => array( 'You have an unresolved setup issue...', 'You have %d unresolved setup issues...', ), '%s added %d inline comment(s).' => array( array( '%s added an inline comment.', '%s added inline comments.', ), ), '%d comment(s)' => array('%d comment', '%d comments'), '%d rejection(s)' => array('%d rejection', '%d rejections'), '%d update(s)' => array('%d update', '%d updates'), 'This configuration value is defined in these %d '. 'configuration source(s): %s.' => array( 'This configuration value is defined in this '. 'configuration source: %2$s.', 'This configuration value is defined in these %d '. 'configuration sources: %s.', ), '%d Open Pull Request(s)' => array( '%d Open Pull Request', '%d Open Pull Requests', ), 'Stale (%s day(s))' => array( 'Stale (%s day)', 'Stale (%s days)', ), 'Old (%s day(s))' => array( 'Old (%s day)', 'Old (%s days)', ), '%s Commit(s)' => array( '%s Commit', '%s Commits', ), '%s attached %d file(s): %s.' => array( array( '%s attached a file: %3$s.', '%s attached files: %3$s.', ), ), '%s detached %d file(s): %s.' => array( array( '%s detached a file: %3$s.', '%s detached files: %3$s.', ), ), '%s changed file(s), attached %d: %s; detached %d: %s.' => '%s changed files, attached: %3$s; detached: %5$s.', '%s added %s dependencie(s): %s.' => array( array( '%s added a dependency: %3$s.', '%s added dependencies: %3$s.', ), ), '%s removed %s dependencie(s): %s.' => array( array( '%s removed a dependency: %3$s.', '%s removed dependencies: %3$s.', ), ), '%s added %s dependent revision(s): %s.' => array( array( '%s added a dependent revision: %3$s.', '%s added dependent revisions: %3$s.', ), ), '%s removed %s dependent revision(s): %s.' => array( array( '%s removed a dependent revision: %3$s.', '%s removed dependent revisions: %3$s.', ), ), '%s added %s commit(s): %s.' => array( array( '%s added a commit: %3$s.', '%s added commits: %3$s.', ), ), '%s removed %s commit(s): %s.' => array( array( '%s removed a commit: %3$s.', '%s removed commits: %3$s.', ), ), '%s edited commit(s), added %s: %s; removed %s: %s.' => '%s edited commits, added %3$s; removed %5$s.', '%s added %s reverted commit(s): %s.' => array( array( '%s added a reverted commit: %3$s.', '%s added reverted commits: %3$s.', ), ), '%s removed %s reverted commit(s): %s.' => array( array( '%s removed a reverted commit: %3$s.', '%s removed reverted commits: %3$s.', ), ), '%s edited reverted commit(s), added %s: %s; removed %s: %s.' => '%s edited reverted commits, added %3$s; removed %5$s.', '%s added %s reverting commit(s): %s.' => array( array( '%s added a reverting commit: %3$s.', '%s added reverting commits: %3$s.', ), ), '%s removed %s reverting commit(s): %s.' => array( array( '%s removed a reverting commit: %3$s.', '%s removed reverting commits: %3$s.', ), ), '%s edited reverting commit(s), added %s: %s; removed %s: %s.' => '%s edited reverting commits, added %3$s; removed %5$s.', '%s changed project member(s), added %d: %s; removed %d: %s.' => '%s changed project members, added %3$s; removed %5$s.', '%s added %d project member(s): %s.' => array( array( '%s added a member: %3$s.', '%s added members: %3$s.', ), ), '%s removed %d project member(s): %s.' => array( array( '%s removed a member: %3$s.', '%s removed members: %3$s.', ), ), '%d project hashtag(s) are already used: %s.' => array( 'Project hashtag %2$s is already used.', '%d project hashtags are already used: %2$s.', ), '%s changed project hashtag(s), added %d: %s; removed %d: %s.' => '%s changed project hashtags, added %3$s; removed %5$s.', '%s added %d project hashtag(s): %s.' => array( array( '%s added a hashtag: %3$s.', '%s added hashtags: %3$s.', ), ), '%s removed %d project hashtag(s): %s.' => array( array( '%s removed a hashtag: %3$s.', '%s removed hashtags: %3$s.', ), ), '%d User(s) Need Approval' => array( '%d User Needs Approval', '%d Users Need Approval', ), '%s older changes(s) are hidden.' => array( '%d older change is hidden.', '%d older changes are hidden.', ), '%s, %s line(s)' => array( '%s, %s line', '%s, %s lines', ), '%s pushed %d commit(s) to %s.' => array( array( '%s pushed a commit to %3$s.', '%s pushed %d commits to %s.', ), ), '%s commit(s)' => array( '1 commit', '%s commits', ), '%s removed %s JIRA issue(s): %s.' => array( array( '%s removed a JIRA issue: %3$s.', '%s removed JIRA issues: %3$s.', ), ), '%s added %s JIRA issue(s): %s.' => array( array( '%s added a JIRA issue: %3$s.', '%s added JIRA issues: %3$s.', ), ), '%s added %s required legal document(s): %s.' => array( array( '%s added a required legal document: %3$s.', '%s added required legal documents: %3$s.', ), ), '%s updated JIRA issue(s): added %s %s; removed %d %s.' => '%s updated JIRA issues: added %3$s; removed %5$s.', '%s edited %s task(s), added %s: %s; removed %s: %s.' => '%s edited tasks, added %4$s; removed %6$s.', '%s added %s task(s) to %s: %s.' => array( array( '%s added a task to %3$s: %4$s.', '%s added tasks to %3$s: %4$s.', ), ), '%s removed %s task(s) from %s: %s.' => array( array( '%s removed a task from %3$s: %4$s.', '%s removed tasks from %3$s: %4$s.', ), ), '%s edited %s task(s) for %s, added %s: %s; removed %s: %s.' => '%s edited tasks for %3$s, added: %5$s; removed %7$s.', '%s edited %s commit(s), added %s: %s; removed %s: %s.' => '%s edited commits, added %4$s; removed %6$s.', '%s added %s commit(s) to %s: %s.' => array( array( '%s added a commit to %3$s: %4$s.', '%s added commits to %3$s: %4$s.', ), ), '%s removed %s commit(s) from %s: %s.' => array( array( '%s removed a commit from %3$s: %4$s.', '%s removed commits from %3$s: %4$s.', ), ), '%s edited %s commit(s) for %s, added %s: %s; removed %s: %s.' => '%s edited commits for %3$s, added: %5$s; removed %7$s.', '%s added %s revision(s): %s.' => array( array( '%s added a revision: %3$s.', '%s added revisions: %3$s.', ), ), '%s removed %s revision(s): %s.' => array( array( '%s removed a revision: %3$s.', '%s removed revisions: %3$s.', ), ), '%s edited %s revision(s), added %s: %s; removed %s: %s.' => '%s edited revisions, added %4$s; removed %6$s.', '%s added %s revision(s) to %s: %s.' => array( array( '%s added a revision to %3$s: %4$s.', '%s added revisions to %3$s: %4$s.', ), ), '%s removed %s revision(s) from %s: %s.' => array( array( '%s removed a revision from %3$s: %4$s.', '%s removed revisions from %3$s: %4$s.', ), ), '%s edited %s revision(s) for %s, added %s: %s; removed %s: %s.' => '%s edited revisions for %3$s, added: %5$s; removed %7$s.', '%s edited %s project(s), added %s: %s; removed %s: %s.' => '%s edited projects, added %4$s; removed %6$s.', '%s added %s project(s) to %s: %s.' => array( array( '%s added a project to %3$s: %4$s.', '%s added projects to %3$s: %4$s.', ), ), '%s removed %s project(s) from %s: %s.' => array( array( '%s removed a project from %3$s: %4$s.', '%s removed projects from %3$s: %4$s.', ), ), '%s edited %s project(s) for %s, added %s: %s; removed %s: %s.' => '%s edited projects for %3$s, added: %5$s; removed %7$s.', '%s added %s panel(s): %s.' => array( array( '%s added a panel: %3$s.', '%s added panels: %3$s.', ), ), '%s removed %s panel(s): %s.' => array( array( '%s removed a panel: %3$s.', '%s removed panels: %3$s.', ), ), '%s edited %s panel(s), added %s: %s; removed %s: %s.' => '%s edited panels, added %4$s; removed %6$s.', '%s added %s dashboard(s): %s.' => array( array( '%s added a dashboard: %3$s.', '%s added dashboards: %3$s.', ), ), '%s removed %s dashboard(s): %s.' => array( array( '%s removed a dashboard: %3$s.', '%s removed dashboards: %3$s.', ), ), '%s edited %s dashboard(s), added %s: %s; removed %s: %s.' => '%s edited dashboards, added %4$s; removed %6$s.', '%s added %s edge(s): %s.' => array( array( '%s added an edge: %3$s.', '%s added edges: %3$s.', ), ), '%s added %s edge(s) to %s: %s.' => array( array( '%s added an edge to %3$s: %4$s.', '%s added edges to %3$s: %4$s.', ), ), '%s removed %s edge(s): %s.' => array( array( '%s removed an edge: %3$s.', '%s removed edges: %3$s.', ), ), '%s removed %s edge(s) from %s: %s.' => array( array( '%s removed an edge from %3$s: %4$s.', '%s removed edges from %3$s: %4$s.', ), ), '%s edited edge(s), added %s: %s; removed %s: %s.' => '%s edited edges, added: %3$s; removed: %5$s.', '%s edited %s edge(s) for %s, added %s: %s; removed %s: %s.' => '%s edited edges for %3$s, added: %5$s; removed %7$s.', '%d related link(s):' => array( 'Related link:', 'Related links:', ), 'You have %d unpaid invoice(s).' => array( 'You have an unpaid invoice.', 'You have unpaid invoices.', ), 'The configurations differ in the following %s way(s):' => array( 'The configurations differ:', 'The configurations differ in these ways:', ), 'Phabricator is configured with an email domain whitelist (in %s), so '. 'only users with a verified email address at one of these %s '. 'allowed domain(s) will be able to register an account: %s' => array( array( 'Phabricator is configured with an email domain whitelist (in %s), '. 'so only users with a verified email address at %3$s will be '. 'allowed to register an account.', 'Phabricator is configured with an email domain whitelist (in %s), '. 'so only users with a verified email address at one of these '. 'allowed domains will be able to register an account: %3$s', ), ), ); } } diff --git a/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php b/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php index 275849d33b..8f3cd3552c 100644 --- a/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php +++ b/src/infrastructure/markup/interpreter/PhabricatorRemarkupCowsayBlockInterpreter.php @@ -1,56 +1,58 @@ markupError( pht('Unable to locate the `cowsay` binary. Install cowsay.')); } $bin = idx($argv, 'think') ? 'cowthink' : 'cowsay'; $eyes = idx($argv, 'eyes', 'oo'); $tongue = idx($argv, 'tongue', ' '); $cow = idx($argv, 'cow', 'default'); // NOTE: Strip this aggressively to prevent nonsense like // `cow=/etc/passwd`. We could build a whiltelist with `cowsay -l`. $cow = preg_replace('/[^a-z.-]+/', '', $cow); $future = new ExecFuture( '%s -e %s -T %s -f %s ', $bin, $eyes, $tongue, $cow); $future->setTimeout(15); $future->write($content); list($err, $stdout, $stderr) = $future->resolve(); if ($err) { return $this->markupError( pht( - 'Execution of `cowsay` failed:', $stderr)); + 'Execution of `%s` failed: %s', + 'cowsay', + $stderr)); } if ($this->getEngine()->isTextMode()) { return $stdout; } return phutil_tag( 'div', array( 'class' => 'PhabricatorMonospaced remarkup-cowsay', ), $stdout); } }