protected function renderActionEffectDescription($type, $data) { switch ($type) { case self::DO_BUILD: return pht('Started %s build(s): %s.', phutil_count($data), $this->renderHandleList($data)); } }
protected function renderActionEffectDescription($type, $data) { switch ($type) { case self::DO_ADD_AUDITORS: return pht('Added %s auditor(s): %s.', phutil_count($data), $this->renderHandleList($data)); } }
public function generateGuidance(PhabricatorGuidanceContext $context) { $domains_key = 'auth.email-domains'; $domains_link = $this->renderConfigLink($domains_key); $domains_value = PhabricatorEnv::getEnvConfig($domains_key); $approval_key = 'auth.require-approval'; $approval_link = $this->renderConfigLink($approval_key); $approval_value = PhabricatorEnv::getEnvConfig($approval_key); $results = array(); if ($domains_value) { $message = pht('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', $domains_link, phutil_count($domains_value), phutil_tag('strong', array(), implode(', ', $domains_value))); $results[] = $this->newGuidance('core.auth.email-domains.on')->setMessage($message); } else { $message = pht('Anyone who can browse to this Phabricator install will be able to ' . 'register an account. To add email domain restrictions, configure ' . '%s.', $domains_link); $results[] = $this->newGuidance('core.auth.email-domains.off')->setMessage($message); } if ($approval_value) { $message = pht('Administrative approvals are enabled (in %s), so all new users must ' . 'have their accounts approved by an administrator.', $approval_link); $results[] = $this->newGuidance('core.auth.require-approval.on')->setMessage($message); } else { $message = pht('Administrative approvals are disabled, so users who register will ' . 'be able to use their accounts immediately. To enable approvals, ' . 'configure %s.', $approval_link); $results[] = $this->newGuidance('core.auth.require-approval.off')->setMessage($message); } if (!$domains_value && !$approval_value) { $message = pht('You can safely ignore these warnings if the install itself has ' . 'access controls (for example, it is deployed on a VPN) or if all of ' . 'the configured providers have access controls (for example, they are ' . 'all private LDAP or OAuth servers).'); $results[] = $this->newWarning('core.auth.warning')->setMessage($message); } return $results; }
protected function renderActionEffectDescription($type, $data) { switch ($type) { case self::DO_SEND: return pht('Queued email to be delivered to %s target(s): %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_FORCE: return pht('Queued email to be delivered to %s target(s), ignoring their ' . 'notification preferences: %s.', phutil_count($data), $this->renderHandleList($data)); } }
protected function renderActionEffectDescription($type, $data) { switch ($type) { case self::DO_SIGNED: return pht('%s document(s) are already signed: %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_REQUIRED: return pht('Required %s signature(s): %s.', phutil_count($data), $this->renderHandleList($data)); } }
protected function renderActionEffectDescription($type, $data) { switch ($type) { case self::DO_ADD_PROJECTS: return pht('Added %s project(s): %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_REMOVE_PROJECTS: return pht('Removed %s project(s): %s.', phutil_count($data), $this->renderHandleList($data)); } }
public function reduceProxyResponse() { $request = $this->getRequest(); $ex = $this->exception; $xactions = $ex->getTransactions(); $type_comment = PhabricatorTransactions::TYPE_COMMENT; $only_empty_comment = count($xactions) == 1 && head($xactions)->getTransactionType() == $type_comment; $count = phutil_count($xactions); if ($ex->hasAnyEffect()) { $title = pht('%s Action(s) With No Effect', $count); $head = pht('Some of your %s action(s) have no effect:', $count); $tail = pht('Apply remaining actions?'); $continue = pht('Apply Remaining Actions'); } else { if ($ex->hasComment()) { $title = pht('Post as Comment'); $head = pht('The %s action(s) you are taking have no effect:', $count); $tail = pht('Do you want to post your comment anyway?'); $continue = pht('Post Comment'); } else { if ($only_empty_comment) { // Special case this since it's common and we can give the user a nicer // dialog than "Action Has No Effect". $title = pht('Empty Comment'); $head = null; $tail = null; $continue = null; } else { $title = pht('%s Action(s) Have No Effect', $count); $head = pht('The %s action(s) you are taking have no effect:', $count); $tail = null; $continue = null; } } } $dialog = id(new AphrontDialogView())->setUser($request->getUser())->setTitle($title); $dialog->appendChild($head); $list = array(); foreach ($xactions as $xaction) { $list[] = $xaction->getNoEffectDescription(); } if ($list) { $dialog->appendList($list); } $dialog->appendChild($tail); if ($continue) { $passthrough = $request->getPassthroughRequestParameters(); foreach ($passthrough as $key => $value) { $dialog->addHiddenInput($key, $value); } $dialog->addHiddenInput('__continue__', 1); $dialog->addSubmitButton($continue); } $dialog->addCancelButton($this->cancelURI); return $this->getProxy()->setDialog($dialog); }
public function execute(PhutilArgumentParser $args) { $resources_map = CelerityPhysicalResources::getAll(); $this->log(pht('Rebuilding %d resource source(s).', phutil_count($resources_map))); foreach ($resources_map as $name => $resources) { $this->rebuildResources($resources); } $this->log(pht('Done.')); return 0; }
public function render() { $viewer = $this->getUser(); $project = $this->getProject(); $user_phids = $this->getUserPHIDs(); $can_edit = $this->canEditList(); $no_data = $this->getNoDataString(); $list = id(new PHUIObjectItemListView())->setNoDataString($no_data); $limit = $this->getLimit(); // If we're showing everything, show oldest to newest. If we're showing // only a slice, show newest to oldest. if (!$limit) { $user_phids = array_reverse($user_phids); } $handles = $viewer->loadHandles($user_phids); // Always put the viewer first if they are on the list. $user_phids = array_fuse($user_phids); $user_phids = array_select_keys($user_phids, array($viewer->getPHID())) + $user_phids; if ($limit) { $render_phids = array_slice($user_phids, 0, $limit); } else { $render_phids = $user_phids; } foreach ($render_phids as $user_phid) { $handle = $handles[$user_phid]; $item = id(new PHUIObjectItemView())->setHeader($handle->getFullName())->setHref($handle->getURI())->setImageURI($handle->getImageURI()); $icon = id(new PHUIIconView())->setIcon($handle->getIcon()); $subtitle = $handle->getSubtitle(); $item->addAttribute(array($icon, ' ', $subtitle)); if ($can_edit && !$limit) { $remove_uri = $this->getRemoveURI($user_phid); $item->addAction(id(new PHUIListItemView())->setIcon('fa-times')->setName(pht('Remove'))->setHref($remove_uri)->setWorkflow(true)); } $list->addItem($item); } if ($user_phids) { $header_text = pht('%s (%s)', $this->getHeaderText(), phutil_count($user_phids)); } else { $header_text = $this->getHeaderText(); } $id = $project->getID(); $header = id(new PHUIHeaderView())->setHeader($header_text); if ($limit) { $header->addActionLink(id(new PHUIButtonView())->setTag('a')->setIcon(id(new PHUIIconView())->setIcon('fa-list-ul'))->setText(pht('View All'))->setHref("/project/members/{$id}/")); } $box = id(new PHUIObjectBoxView())->setHeader($header)->setObjectList($list); if ($this->background) { $box->setBackground($this->background); } return $box; }
public function process(XHPASTNode $root) { $classes = $root->selectDescendantsOfType('n_CLASS_DECLARATION'); foreach ($classes as $class) { $class_modifiers = $this->getModifiers($class); $abstract_methods = array(); $methods = $class->selectDescendantsOfType('n_METHOD_DECLARATION'); foreach ($methods as $method) { $method_modifiers = $this->getModifiers($method); if (idx($method_modifiers, 'abstract')) { $abstract_methods[] = $method; } } if (!idx($class_modifiers, 'abstract') && $abstract_methods) { $this->raiseLintAtNode($class, pht('Class contains %s %s method(s) and must therefore ' . 'be declared `%s`.', phutil_count($abstract_methods), 'abstract', 'abstract')); } } }
public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case self::TYPE_TITLE: case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_JOIN_POLICY: case self::TYPE_PICTURE: return $this->getRoomTitle(); break; case self::TYPE_FILES: $add = array_diff($new, $old); $rem = array_diff($old, $new); if ($add && $rem) { $title = pht('%s edited files(s), added %d and removed %d.', $this->renderHandleLink($author_phid), count($add), count($rem)); } else { if ($add) { $title = pht('%s added %s files(s).', $this->renderHandleLink($author_phid), phutil_count($add)); } else { $title = pht('%s removed %s file(s).', $this->renderHandleLink($author_phid), phutil_count($rem)); } } return $title; break; case self::TYPE_PARTICIPANTS: $add = array_diff($new, $old); $rem = array_diff($old, $new); if ($add && $rem) { $title = pht('%s edited participant(s), added %d: %s; removed %d: %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add), count($rem), $this->renderHandleList($rem)); } else { if ($add) { $title = pht('%s added %d participant(s): %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add)); } else { $title = pht('%s removed %d participant(s): %s.', $this->renderHandleLink($author_phid), count($rem), $this->renderHandleList($rem)); } } return $title; break; } return parent::getTitle(); }
private function extractLibrary($root) { $files = $this->loadLibraryFiles($root); $cache = $this->readCache($root); $modified = $this->getModifiedFiles($files, $cache); $cache['files'] = $files; if ($modified) { echo tsprintf("**<bg:blue> %s </bg>** %s\n", pht('MODIFIED'), pht('Found %s modified file(s) (of %s total).', phutil_count($modified), phutil_count($files))); $old_strings = idx($cache, 'strings'); $old_strings = array_select_keys($old_strings, $files); $new_strings = $this->extractFiles($root, $modified); $all_strings = $new_strings + $old_strings; $cache['strings'] = $all_strings; $this->writeStrings($root, $all_strings); } else { echo tsprintf("**<bg:blue> %s </bg>** %s\n", pht('NOT MODIFIED'), pht('Strings for this library are already up to date.')); } $cache = id(new PhutilJSON())->encodeFormatted($cache); $this->writeCache($root, 'i18n_files.json', $cache); }
private function buildProjectsView(PhabricatorUser $user) { $viewer = $this->getViewer(); $projects = id(new PhabricatorProjectQuery())->setViewer($viewer)->withMemberPHIDs(array($user->getPHID()))->needImages(true)->withStatuses(array(PhabricatorProjectStatus::STATUS_ACTIVE))->execute(); $header = id(new PHUIHeaderView())->setHeader(pht('Projects')); if (!empty($projects)) { $limit = 5; $render_phids = array_slice($projects, 0, $limit); $list = id(new PhabricatorProjectListView())->setUser($viewer)->setProjects($render_phids); if (count($projects) > $limit) { $header_text = pht('Projects (%s)', phutil_count($projects)); $header = id(new PHUIHeaderView())->setHeader($header_text)->addActionLink(id(new PHUIButtonView())->setTag('a')->setIcon('fa-list-ul')->setText(pht('View All'))->setHref('/project/?member=' . $user->getPHID())); } } else { $error = id(new PHUIBoxView())->addClass('mlb')->appendChild(pht('User does not belong to any projects.')); $list = id(new PHUIInfoView())->setSeverity(PHUIInfoView::SEVERITY_NODATA)->appendChild($error); } $box = id(new PHUIObjectBoxView())->setHeader($header)->appendChild($list)->setBackground(PHUIObjectBoxView::GREY); return $box; }
protected function executeChecks() { $task_daemon = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_RUNNING)->withDaemonClasses(array('PhabricatorTaskmasterDaemon'))->setLimit(1)->execute(); if (!$task_daemon) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('You must start the Phabricator daemons to send email, rebuild ' . 'search indexes, and do other background processing.'); $message = pht('The Phabricator daemons are not running, so Phabricator will not ' . 'be able to perform background processing (including sending email, ' . 'rebuilding search indexes, importing commits, cleaning up old data, ' . 'and running builds).' . "\n\n" . 'Use %s to start daemons. See %s for more information.', phutil_tag('tt', array(), 'bin/phd start'), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Managing Daemons with phd'))); $this->newIssue('daemons.not-running')->setShortName(pht('Daemons Not Running'))->setName(pht('Phabricator Daemons Are Not Running'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd start'); } $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); $environment_hash = PhabricatorEnv::calculateEnvironmentHash(); $all_daemons = id(new PhabricatorDaemonLogQuery())->setViewer(PhabricatorUser::getOmnipotentUser())->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE)->execute(); foreach ($all_daemons as $daemon) { if ($phd_user) { if ($daemon->getRunningAsUser() != $phd_user) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('At least one daemon is currently running as a different ' . 'user than configured in the Phabricator %s setting', 'phd.user'); $message = pht('A daemon is running as user %s while the Phabricator config ' . 'specifies %s to be %s.' . "\n\n" . 'Either adjust %s to match %s or start ' . 'the daemons as the correct user. ' . "\n\n" . '%s Daemons will try to use %s to start as the configured user. ' . 'Make sure that the user who starts %s has the correct ' . 'sudo permissions to start %s daemons as %s', 'phd.user', 'phd.user', 'phd', 'sudo', 'phd', 'phd', phutil_tag('tt', array(), $daemon->getRunningAsUser()), phutil_tag('tt', array(), $phd_user), phutil_tag('tt', array(), $daemon->getRunningAsUser()), phutil_tag('tt', array(), $phd_user)); $this->newIssue('daemons.run-as-different-user')->setName(pht('Daemons are running as the wrong user'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd restart'); } } if ($daemon->getEnvHash() != $environment_hash) { $doc_href = PhabricatorEnv::getDocLink('Managing Daemons with phd'); $summary = pht('At least one daemon is currently running with different ' . 'configuration than the Phabricator web application.'); $list_section = null; $env_info = $daemon->getEnvInfo(); if ($env_info) { $issues = PhabricatorEnv::compareEnvironmentInfo(PhabricatorEnv::calculateEnvironmentInfo(), $env_info); if ($issues) { foreach ($issues as $key => $issue) { $issues[$key] = phutil_tag('li', array(), $issue); } $list_section = array(pht('The configurations differ in the following %s way(s):', phutil_count($issues)), phutil_tag('ul', array(), $issues)); } } $message = pht('At least one daemon is currently running with a different ' . 'configuration (config checksum %s) than the web application ' . '(config checksum %s).' . "\n\n%s" . 'This usually means that you have just made a configuration change ' . 'from the web UI, but have not yet restarted the daemons. You ' . 'need to restart the daemons after making configuration changes ' . 'so they will pick up the new values: until you do, they will ' . 'continue operating with the old settings.' . "\n\n" . '(If you plan to make more changes, you can restart the daemons ' . 'once after you finish making all of your changes.)' . "\n\n" . 'Use %s to restart daemons. You can find a list of running daemons ' . 'in the %s, which will also help you identify which daemon (or ' . 'daemons) have divergent configuration. For more information about ' . 'managing the daemons, see %s in the documentation.' . "\n\n" . 'This can also happen if you use the %s environmental variable to ' . 'choose a configuration file, but the daemons run with a different ' . 'value than the web application. If restarting the daemons does ' . 'not resolve this issue and you use %s to select configuration, ' . 'check that it is set consistently.' . "\n\n" . 'A third possible cause is that you run several machines, and ' . 'the %s configuration file differs between them. This file is ' . 'updated when you edit configuration from the CLI with %s. If ' . 'restarting the daemons does not resolve this issue and you ' . 'run multiple machines, check that all machines have identical ' . '%s configuration files.' . "\n\n" . 'This issue is not severe, but usually indicates that something ' . 'is not configured the way you expect, and may cause the daemons ' . 'to exhibit different behavior than the web application does.', phutil_tag('tt', array(), substr($daemon->getEnvHash(), 0, 12)), phutil_tag('tt', array(), substr($environment_hash, 0, 12)), $list_section, phutil_tag('tt', array(), 'bin/phd restart'), phutil_tag('a', array('href' => '/daemon/', 'target' => '_blank'), pht('Daemon Console')), phutil_tag('a', array('href' => $doc_href, 'target' => '_blank'), pht('Managing Daemons with phd')), phutil_tag('tt', array(), 'PHABRICATOR_ENV'), phutil_tag('tt', array(), 'PHABRICATOR_ENV'), phutil_tag('tt', array(), 'phabricator/conf/local/local.json'), phutil_tag('tt', array(), 'bin/config'), phutil_tag('tt', array(), 'phabricator/conf/local/local.json')); $this->newIssue('daemons.need-restarting')->setName(pht('Daemons and Web Have Different Config'))->setSummary($summary)->setMessage($message)->addCommand('phabricator/ $ ./bin/phd restart'); break; } } }
public function render() { $viewer = $this->getUser(); $tasks = $this->tasks; $query = $this->savedQuery; // If we didn't match anything, just pick up the default empty state. if (!$tasks) { return id(new PHUIObjectItemListView())->setUser($viewer)->setNoDataString(pht('No tasks found.')); } $group_parameter = nonempty($query->getParameter('group'), 'priority'); $order_parameter = nonempty($query->getParameter('order'), 'priority'); $handles = ManiphestTaskListView::loadTaskHandles($viewer, $tasks); $groups = $this->groupTasks($tasks, $group_parameter, $handles); $can_edit_priority = $this->canEditPriority; $can_drag = $order_parameter == 'priority' && $can_edit_priority && ($group_parameter == 'none' || $group_parameter == 'priority'); if (!$viewer->isLoggedIn()) { // TODO: (T7131) Eventually, we conceivably need to make each task // draggable individually, since the user may be able to edit some but // not others. $can_drag = false; } $result = array(); $lists = array(); foreach ($groups as $group => $list) { $task_list = new ManiphestTaskListView(); $task_list->setShowBatchControls($this->showBatchControls); if ($can_drag) { $task_list->setShowSubpriorityControls(true); } $task_list->setUser($viewer); $task_list->setTasks($list); $task_list->setHandles($handles); $header = id(new PHUIHeaderView())->addSigil('task-group')->setMetadata(array('priority' => head($list)->getPriority()))->setHeader(pht('%s (%s)', $group, phutil_count($list))); $lists[] = id(new PHUIObjectBoxView())->setHeader($header)->setObjectList($task_list); } if ($can_drag) { Javelin::initBehavior('maniphest-subpriority-editor', array('uri' => '/maniphest/subpriority/')); } return array($lists, $this->showBatchControls ? $this->renderBatchEditor($query) : null); }
function commit_symbols(array $symbols, PhabricatorRepository $repository, $no_purge) { echo pht('Looking up path IDs...'), "\n"; $path_map = PhabricatorRepositoryCommitChangeParserWorker::lookupOrCreatePaths(ipull($symbols, 'path')); $symbol = new PhabricatorRepositorySymbol(); $conn_w = $symbol->establishConnection('w'); echo pht('Preparing queries...'), "\n"; $sql = array(); foreach ($symbols as $dict) { $sql[] = qsprintf($conn_w, '(%s, %s, %s, %s, %s, %d, %d)', $repository->getPHID(), $dict['ctxt'], $dict['name'], $dict['type'], $dict['lang'], $dict['line'], $path_map[$dict['path']]); } if (!$no_purge) { echo pht('Purging old symbols...'), "\n"; queryfx($conn_w, 'DELETE FROM %T WHERE repositoryPHID = %s', $symbol->getTableName(), $repository->getPHID()); } echo pht('Loading %s symbols...', phutil_count($sql)), "\n"; foreach (array_chunk($sql, 128) as $chunk) { queryfx($conn_w, 'INSERT INTO %T (repositoryPHID, symbolContext, symbolName, symbolType, symbolLanguage, lineNumber, pathID) VALUES %Q', $symbol->getTableName(), implode(', ', $chunk)); } }
public function getTitleForFeed() { list($add, $rem) = $this->getChanges(); if ($add && !$rem) { return pht('%s invited %s attendee(s) to %s: %s.', $this->renderAuthor(), phutil_count($add), $this->renderObject(), $this->renderHandleList($add)); } else { if (!$add && $rem) { return pht('%s uninvited %s attendee(s) to %s: %s.', $this->renderAuthor(), phutil_count($rem), $this->renderObject(), $this->renderHandleList($rem)); } else { return pht('%s updated the invite list for %s, invited %s: %s; ' . 'uninvinted %s: %s.', $this->renderAuthor(), $this->renderObject(), phutil_count($add), $this->renderHandleList($add), phutil_count($rem), $this->renderHandleList($rem)); } } }
private function parseParams(DivinerAtom $atom, AASTNode $func) { $params = $func->getChildByIndex(3, 'n_DECLARATAION_PARAMETER_LIST')->selectDescendantsOfType('n_DECLARATION_PARAMETER'); $param_spec = array(); if ($atom->getDocblockRaw()) { $metadata = $atom->getDocblockMeta(); } else { $metadata = array(); } $docs = idx($metadata, 'param'); if ($docs) { $docs = (array) $docs; $docs = array_filter($docs); } else { $docs = array(); } if (count($docs)) { if (count($docs) < count($params)) { $atom->addWarning(pht('This call takes %s parameter(s), but only %s are documented.', phutil_count($params), phutil_count($docs))); } } foreach ($params as $param) { $name = $param->getChildByIndex(1)->getConcreteString(); $dict = array('type' => $param->getChildByIndex(0)->getConcreteString(), 'default' => $param->getChildByIndex(2)->getConcreteString()); if ($docs) { $doc = array_shift($docs); if ($doc) { $dict += $this->parseParamDoc($atom, $doc, $name); } } $param_spec[] = array('name' => $name) + $dict; } if ($docs) { foreach ($docs as $doc) { if ($doc) { $param_spec[] = $this->parseParamDoc($atom, $doc, null); } } } // TODO: Find `assert_instances_of()` calls in the function body and // add their type information here. See T1089. $atom->setProperty('parameters', $param_spec); }
protected function renderActionEffectDescription($type, $data) { switch ($type) { case self::DO_PREVIOUSLY_UNSUBSCRIBED: return pht('Declined to resubscribe %s target(s) because they previously ' . 'unsubscribed: %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_AUTOSUBSCRIBED: return pht('%s automatically subscribed target(s) were not affected: %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_SUBSCRIBED: return pht('Added %s subscriber(s): %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_UNSUBSCRIBED: return pht('Removed %s subscriber(s): %s.', phutil_count($data), $this->renderHandleList($data)); } }
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*</td><td class="v">(.*?)</td>@i'; $matches = null; $ini_loc = null; if (preg_match($rex, $phpinfo, $matches)) { $ini_loc = trim($matches[1]); } $rex = '@Additional \\.ini files parsed\\s*</td><td class="v">(.*?)</td>@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 %s configuration file(s):', phutil_count($more_loc))); $info[] = phutil_tag('pre', array(), implode("\n", $more_loc)); } $show_standard = false; $show_opcache = false; foreach ($configs as $key) { if (preg_match('/^opcache\\./', $key)) { $show_opcache = true; } else { $show_standard = true; } } if ($show_standard) { $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')))); } if ($show_opcache) { $info[] = phutil_tag('p', array(), pht('You can find more information about configuring OPCache in ' . 'the %s.', phutil_tag('a', array('href' => 'http://php.net/manual/opcache.configuration.php', 'target' => '_blank'), pht('PHP OPCache Documentation')))); } $info[] = phutil_tag('p', array(), pht('After editing the PHP configuration, <strong>restart Phabricator for ' . 'the changes to take effect</strong>. For help with restarting ' . 'Phabricator, see %s in the documentation.', $this->renderRestartLink())); return phutil_tag('div', array('class' => 'setup-issue-config'), array($table_info, $table, $info)); }
public function processRequest() { $request = $this->getRequest(); $viewer = $request->getUser(); $configs = id(new PhabricatorAuthProviderConfigQuery())->setViewer($viewer)->execute(); $list = new PHUIObjectItemListView(); $can_manage = $this->hasApplicationCapability(AuthManageProvidersCapability::CAPABILITY); foreach ($configs as $config) { $item = new PHUIObjectItemView(); $id = $config->getID(); $edit_uri = $this->getApplicationURI('config/edit/' . $id . '/'); $enable_uri = $this->getApplicationURI('config/enable/' . $id . '/'); $disable_uri = $this->getApplicationURI('config/disable/' . $id . '/'); $provider = $config->getProvider(); if ($provider) { $name = $provider->getProviderName(); } else { $name = $config->getProviderType() . ' (' . $config->getProviderClass() . ')'; } $item->setHeader($name); if ($provider) { $item->setHref($edit_uri); } else { $item->addAttribute(pht('Provider Implementation Missing!')); } $domain = null; if ($provider) { $domain = $provider->getProviderDomain(); if ($domain !== 'self') { $item->addAttribute($domain); } } if ($config->getShouldAllowRegistration()) { $item->addAttribute(pht('Allows Registration')); } else { $item->addAttribute(pht('Does Not Allow Registration')); } if ($config->getIsEnabled()) { $item->setState(PHUIObjectItemView::STATE_SUCCESS); $item->addAction(id(new PHUIListItemView())->setIcon('fa-times')->setHref($disable_uri)->setDisabled(!$can_manage)->addSigil('workflow')); } else { $item->setState(PHUIObjectItemView::STATE_FAIL); $item->addIcon('fa-times grey', pht('Disabled')); $item->addAction(id(new PHUIListItemView())->setIcon('fa-plus')->setHref($enable_uri)->setDisabled(!$can_manage)->addSigil('workflow')); } $list->addItem($item); } $list->setNoDataString(pht('%s You have not added authentication providers yet. Use "%s" to add ' . 'a provider, which will let users register new Phabricator accounts ' . 'and log in.', phutil_tag('strong', array(), pht('No Providers Configured:')), phutil_tag('a', array('href' => $this->getApplicationURI('config/new/')), pht('Add Authentication Provider')))); $crumbs = $this->buildApplicationCrumbs(); $crumbs->addTextCrumb(pht('Auth Providers')); $domains_key = 'auth.email-domains'; $domains_link = $this->renderConfigLink($domains_key); $domains_value = PhabricatorEnv::getEnvConfig($domains_key); $approval_key = 'auth.require-approval'; $approval_link = $this->renderConfigLink($approval_key); $approval_value = PhabricatorEnv::getEnvConfig($approval_key); $issues = array(); if ($domains_value) { $issues[] = pht('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', $domains_link, phutil_count($domains_value), phutil_tag('strong', array(), implode(', ', $domains_value))); } else { $issues[] = pht('Anyone who can browse to this Phabricator install will be able to ' . 'register an account. To add email domain restrictions, configure ' . '%s.', $domains_link); } if ($approval_value) { $issues[] = pht('Administrative approvals are enabled (in %s), so all new users must ' . 'have their accounts approved by an administrator.', $approval_link); } else { $issues[] = pht('Administrative approvals are disabled, so users who register will ' . 'be able to use their accounts immediately. To enable approvals, ' . 'configure %s.', $approval_link); } if (!$domains_value && !$approval_value) { $severity = PHUIInfoView::SEVERITY_WARNING; $issues[] = pht('You can safely ignore this warning if the install itself has ' . 'access controls (for example, it is deployed on a VPN) or if all of ' . 'the configured providers have access controls (for example, they are ' . 'all private LDAP or OAuth servers).'); } else { $severity = PHUIInfoView::SEVERITY_NOTICE; } $warning = id(new PHUIInfoView())->setSeverity($severity)->setErrors($issues); $image = id(new PHUIIconView())->setIconFont('fa-plus'); $button = id(new PHUIButtonView())->setTag('a')->setColor(PHUIButtonView::SIMPLE)->setHref($this->getApplicationURI('config/new/'))->setIcon($image)->setDisabled(!$can_manage)->setText(pht('Add Provider')); $header = id(new PHUIHeaderView())->setHeader(pht('Authentication Providers'))->addActionLink($button); $list->setFlush(true); $list = id(new PHUIObjectBoxView())->setHeader($header)->setInfoView($warning)->appendChild($list); return $this->buildApplicationPage(array($crumbs, $list), array('title' => pht('Authentication Providers'))); }
protected function getCommitFileList(array $revision) { $repository_api = $this->getRepositoryAPI(); $revision_id = $revision['id']; $commit_paths = $this->getConduit()->callMethodSynchronous('differential.getcommitpaths', array('revision_id' => $revision_id)); $dir_paths = array(); foreach ($commit_paths as $path) { $path = dirname($path); while ($path != '.') { $dir_paths[$path] = true; $path = dirname($path); } } $commit_paths = array_fill_keys($commit_paths, true); $status = $repository_api->getSVNStatus(); $modified_but_not_included = array(); foreach ($status as $path => $mask) { if (!empty($dir_paths[$path])) { $commit_paths[$path] = true; } if (!empty($commit_paths[$path])) { continue; } foreach ($commit_paths as $will_commit => $ignored) { if (Filesystem::isDescendant($path, $will_commit)) { throw new ArcanistUsageException(pht("This commit includes the directory '%s', but it contains a " . "modified path ('%s') which is NOT included in the commit. " . "Subversion can not handle this operation and will commit the " . "path anyway. You need to sort out the working copy changes to " . "'%s' before you may proceed with the commit.", $will_commit, $path, $path)); } } $modified_but_not_included[] = $path; } if ($modified_but_not_included) { $prefix = pht('%s locally modified path(s) are not included in this revision:', phutil_count($modified_but_not_included)); $prompt = pht('These %s path(s) will NOT be committed. Commit this revision anyway?', phutil_count($modified_but_not_included)); $this->promptFileWarning($prefix, $prompt, $modified_but_not_included); } $do_not_exist = array(); foreach ($commit_paths as $path => $ignored) { $disk_path = $repository_api->getPath($path); if (file_exists($disk_path)) { continue; } if (is_link($disk_path)) { continue; } if (idx($status, $path) & ArcanistRepositoryAPI::FLAG_DELETED) { continue; } $do_not_exist[] = $path; unset($commit_paths[$path]); } if ($do_not_exist) { $prefix = pht('Revision includes changes to %s path(s) that do not exist:', phutil_count($do_not_exist)); $prompt = pht('Commit this revision anyway?'); $this->promptFileWarning($prefix, $prompt, $do_not_exist); } $files = array_keys($commit_paths); $files = ArcanistSubversionAPI::escapeFileNamesForSVN($files); if (empty($files)) { throw new ArcanistUsageException(pht('There is nothing left to commit. ' . 'None of the modified paths exist.')); } return $files; }
/** * Upload missing chunks of a large file by calling `file.uploadchunk` over * Conduit. * * @task internal */ private function uploadChunks(ArcanistFileDataRef $file, $file_phid) { $conduit = $this->conduit; $chunks = $conduit->callMethodSynchronous('file.querychunks', array('filePHID' => $file_phid)); $remaining = array(); foreach ($chunks as $chunk) { if (!$chunk['complete']) { $remaining[] = $chunk; } } $done = count($chunks) - count($remaining); if ($done) { $this->writeStatus(pht('Resuming upload (%s of %s chunks remain).', phutil_count($remaining), phutil_count($chunks))); } else { $this->writeStatus(pht('Uploading chunks (%s chunks to upload).', phutil_count($remaining))); } $progress = new PhutilConsoleProgressBar(); $progress->setTotal(count($chunks)); for ($ii = 0; $ii < $done; $ii++) { $progress->update(1); } $progress->draw(); // TODO: We could do these in parallel to improve upload performance. foreach ($remaining as $chunk) { $data = $file->readBytes($chunk['byteStart'], $chunk['byteEnd']); $conduit->callMethodSynchronous('file.uploadchunk', array('filePHID' => $file_phid, 'byteStart' => $chunk['byteStart'], 'dataEncoding' => 'base64', 'data' => base64_encode($data))); $progress->update(1); } }
protected function validateTransaction(PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case PhabricatorProjectTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField($object->getName(), $xactions); if ($missing) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Required'), pht('Project name is required.'), nonempty(last($xactions), null)); $error->setIsMissingFieldError(true); $errors[] = $error; } if (!$xactions) { break; } if ($this->getIsMilestone()) { break; } $name = last($xactions)->getNewValue(); if (!PhabricatorSlug::isValidProjectSlug($name)) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Project names must contain at least one letter or number.'), last($xactions)); break; } $slug = PhabricatorSlug::normalizeProjectSlug($name); $slug_used_already = id(new PhabricatorProjectSlug())->loadOneWhere('slug = %s', $slug); if ($slug_used_already && $slug_used_already->getProjectPHID() != $object->getPHID()) { $error = new PhabricatorApplicationTransactionValidationError($type, pht('Duplicate'), pht('Project name generates the same hashtag ("%s") as another ' . 'existing project. Choose a unique name.', '#' . $slug), nonempty(last($xactions), null)); $errors[] = $error; } break; case PhabricatorProjectTransaction::TYPE_SLUGS: if (!$xactions) { break; } $slug_xaction = last($xactions); $new = $slug_xaction->getNewValue(); $invalid = array(); foreach ($new as $slug) { if (!PhabricatorSlug::isValidProjectSlug($slug)) { $invalid[] = $slug; } } if ($invalid) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Hashtags must contain at least one letter or number. %s ' . 'project hashtag(s) are invalid: %s.', phutil_count($invalid), implode(', ', $invalid)), $slug_xaction); break; } $new = $this->normalizeSlugs($new); if ($new) { $slugs_used_already = id(new PhabricatorProjectSlug())->loadAllWhere('slug IN (%Ls)', $new); } else { // The project doesn't have any extra slugs. $slugs_used_already = array(); } $slugs_used_already = mgroup($slugs_used_already, 'getProjectPHID'); foreach ($slugs_used_already as $project_phid => $used_slugs) { if ($project_phid == $object->getPHID()) { continue; } $used_slug_strs = mpull($used_slugs, 'getSlug'); $error = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('%s project hashtag(s) are already used by other projects: %s.', phutil_count($used_slug_strs), implode(', ', $used_slug_strs)), $slug_xaction); $errors[] = $error; } break; case PhabricatorProjectTransaction::TYPE_PARENT: case PhabricatorProjectTransaction::TYPE_MILESTONE: if (!$xactions) { break; } $xaction = last($xactions); $parent_phid = $xaction->getNewValue(); if (!$parent_phid) { continue; } if (!$this->getIsNewObject()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can only set a parent or milestone project when creating a ' . 'project for the first time.'), $xaction); break; } $projects = id(new PhabricatorProjectQuery())->setViewer($this->requireActor())->withPHIDs(array($parent_phid))->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->execute(); if (!$projects) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Parent or milestone project PHID ("%s") must be the PHID of a ' . 'valid, visible project which you have permission to edit.', $parent_phid), $xaction); break; } $project = head($projects); if ($project->isMilestone()) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('Parent or milestone project PHID ("%s") must not be a ' . 'milestone. Milestones may not have subprojects or milestones.', $parent_phid), $xaction); break; } $limit = PhabricatorProject::getProjectDepthLimit(); if ($project->getProjectDepth() >= $limit - 1) { $errors[] = new PhabricatorApplicationTransactionValidationError($type, pht('Invalid'), pht('You can not create a subproject or mielstone under this parent ' . 'because it would nest projects too deeply. The maximum ' . 'nesting depth of projects is %s.', new PhutilNumber($limit)), $xaction); break; } $object->attachParentProject($project); break; } return $errors; }
public function getTitleForFeed() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { case PhabricatorTransactions::TYPE_CREATE: return pht('%s created %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_COMMENT: return pht('%s added a comment to %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_VIEW_POLICY: return pht('%s changed the visibility for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_EDIT_POLICY: return pht('%s changed the edit policy for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_JOIN_POLICY: return pht('%s changed the join policy for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_SUBSCRIBERS: return pht('%s updated subscribers of %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); case PhabricatorTransactions::TYPE_SPACE: return pht('%s shifted %s from the %s space to the %s space.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($old), $this->renderHandleLink($new)); case PhabricatorTransactions::TYPE_EDGE: $new = ipull($new, 'dst'); $old = ipull($old, 'dst'); $add = array_diff($new, $old); $rem = array_diff($old, $new); $type = $this->getMetadata('edge:type'); $type = head($type); $type_obj = PhabricatorEdgeType::getByConstant($type); if ($add && $rem) { return $type_obj->getFeedEditString($this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), new PhutilNumber(count($add) + count($rem)), phutil_count($add), $this->renderHandleList($add), phutil_count($rem), $this->renderHandleList($rem)); } else { if ($add) { return $type_obj->getFeedAddString($this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), phutil_count($add), $this->renderHandleList($add)); } else { if ($rem) { return $type_obj->getFeedRemoveString($this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), phutil_count($rem), $this->renderHandleList($rem)); } else { return pht('%s edited edge metadata for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } } } case PhabricatorTransactions::TYPE_CUSTOMFIELD: $field = $this->getTransactionCustomField(); if ($field) { return $field->getApplicationTransactionTitleForFeed($this); } else { return pht('%s edited a custom field on %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); } case PhabricatorTransactions::TYPE_BUILDABLE: switch ($this->getNewValue()) { case HarbormasterBuildable::STATUS_BUILDING: return pht('%s started building %s for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($this->getMetadataValue('harbormaster:buildablePHID')), $this->renderHandleLink($object_phid)); case HarbormasterBuildable::STATUS_PASSED: return pht('%s completed building %s for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($this->getMetadataValue('harbormaster:buildablePHID')), $this->renderHandleLink($object_phid)); case HarbormasterBuildable::STATUS_FAILED: return pht('%s failed to build %s for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($this->getMetadataValue('harbormaster:buildablePHID')), $this->renderHandleLink($object_phid)); default: return null; } case PhabricatorTransactions::TYPE_COLUMNS: $moves = $this->getInterestingMoves($new); if (count($moves) == 1) { $move = head($moves); $from_columns = $move['fromColumnPHIDs']; $to_column = $move['columnPHID']; $board_phid = $move['boardPHID']; if (count($from_columns) == 1) { return pht('%s moved %s from %s to %s on the %s board.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink(head($from_columns)), $this->renderHandleLink($to_column), $this->renderHandleLink($board_phid)); } else { return pht('%s moved %s to %s on the %s board.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), $this->renderHandleLink($to_column), $this->renderHandleLink($board_phid)); } } else { $fragments = array(); foreach ($moves as $move) { $fragments[] = pht('%s (%s)', $this->renderHandleLink($board_phid), $this->renderHandleLink($to_column)); } return pht('%s moved %s on %s board(s): %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid), phutil_count($moves), phutil_implode_html(', ', $fragments)); } break; } return $this->getTitle(); }
public final function renderEffectDescription($type, $data) { $result = $this->renderActionEffectDescription($type, $data); if ($result !== null) { return $result; } switch ($type) { case self::DO_STANDARD_EMPTY: return pht('This action specifies no targets.'); case self::DO_STANDARD_NO_EFFECT: return pht('This action has no effect on %s target(s): %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_STANDARD_INVALID: return pht('%s target(s) are invalid or of the wrong type: %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_STANDARD_UNLOADABLE: return pht('%s target(s) could not be loaded: %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_STANDARD_PERMISSION: return pht('%s target(s) do not have permission to see this object: %s.', phutil_count($data), $this->renderHandleList($data)); case self::DO_STANDARD_INVALID_ACTION: return pht('No implementation is available for rule "%s".', $data); case self::DO_STANDARD_WRONG_RULE_TYPE: return pht('This action does not support rules of type "%s".', $data); } return null; }
protected function generateChanges() { $parser = $this->newDiffParser(); $is_raw = $this->isRawDiffSource(); if ($is_raw) { if ($this->getArgument('raw')) { fwrite(STDERR, pht('Reading diff from stdin...') . "\n"); $raw_diff = file_get_contents('php://stdin'); } else { if ($this->getArgument('raw-command')) { list($raw_diff) = execx('%C', $this->getArgument('raw-command')); } else { throw new Exception(pht('Unknown raw diff source.')); } } $changes = $parser->parseDiff($raw_diff); foreach ($changes as $key => $change) { // Remove "message" changes, e.g. from "git show". if ($change->getType() == ArcanistDiffChangeType::TYPE_MESSAGE) { unset($changes[$key]); } } return $changes; } $repository_api = $this->getRepositoryAPI(); if ($repository_api instanceof ArcanistSubversionAPI) { $paths = $this->generateAffectedPaths(); $this->primeSubversionWorkingCopyData($paths); // Check to make sure the user is diffing from a consistent base revision. // This is mostly just an abuse sanity check because it's silly to do this // and makes the code more difficult to effectively review, but it also // affects patches and makes them nonportable. $bases = $repository_api->getSVNBaseRevisions(); // Remove all files with baserev "0"; these files are new. foreach ($bases as $path => $baserev) { if ($bases[$path] <= 0) { unset($bases[$path]); } } if ($bases) { $rev = reset($bases); $revlist = array(); foreach ($bases as $path => $baserev) { $revlist[] = ' ' . pht('Revision %s, %s', $baserev, $path); } $revlist = implode("\n", $revlist); foreach ($bases as $path => $baserev) { if ($baserev !== $rev) { throw new ArcanistUsageException(pht("Base revisions of changed paths are mismatched. Update all " . "paths to the same base revision before creating a diff: " . "\n\n%s", $revlist)); } } // If you have a change which affects several files, all of which are // at a consistent base revision, treat that revision as the effective // base revision. The use case here is that you made a change to some // file, which updates it to HEAD, but want to be able to change it // again without updating the entire working copy. This is a little // sketchy but it arises in Facebook Ops workflows with config files and // doesn't have any real material tradeoffs (e.g., these patches are // perfectly applyable). $repository_api->overrideSVNBaseRevisionNumber($rev); } $changes = $parser->parseSubversionDiff($repository_api, $paths); } else { if ($repository_api instanceof ArcanistGitAPI) { $diff = $repository_api->getFullGitDiff($repository_api->getBaseCommit(), $repository_api->getHeadCommit()); if (!strlen($diff)) { throw new ArcanistUsageException(pht('No changes found. (Did you specify the wrong commit range?)')); } $changes = $parser->parseDiff($diff); } else { if ($repository_api instanceof ArcanistMercurialAPI) { $diff = $repository_api->getFullMercurialDiff(); if (!strlen($diff)) { throw new ArcanistUsageException(pht('No changes found. (Did you specify the wrong commit range?)')); } $changes = $parser->parseDiff($diff); } else { throw new Exception(pht('Repository API is not supported.')); } } } if (count($changes) > 250) { $message = pht('This diff has a very large number of changes (%s). Differential ' . 'works best for changes which will receive detailed human review, ' . 'and not as well for large automated changes or bulk checkins. ' . 'See %s for information about reviewing big checkins. Continue anyway?', phutil_count($changes), 'https://secure.phabricator.com/book/phabricator/article/' . 'differential_large_changes/'); if (!phutil_console_confirm($message)) { throw new ArcanistUsageException(pht('Aborted generation of gigantic diff.')); } } $limit = 1024 * 1024 * 4; foreach ($changes as $change) { $size = 0; foreach ($change->getHunks() as $hunk) { $size += strlen($hunk->getCorpus()); } if ($size > $limit) { $byte_warning = pht("Diff for '%s' with context is %s bytes in length. " . "Generally, source changes should not be this large.", $change->getCurrentPath(), new PhutilNumber($size)); if (!$this->getArgument('less-context')) { $byte_warning .= ' ' . pht("If this file is a huge text file, try using the '%s' flag.", '--less-context'); } if ($repository_api instanceof ArcanistSubversionAPI) { throw new ArcanistUsageException($byte_warning . ' ' . pht("If the file is not a text file, mark it as binary with:" . "\n\n \$ %s\n", 'svn propset svn:mime-type application/octet-stream <filename>')); } else { $confirm = $byte_warning . ' ' . pht("If the file is not a text file, you can mark it 'binary'. " . "Mark this file as 'binary' and continue?"); if (phutil_console_confirm($confirm)) { $change->convertToBinaryChange($repository_api); } else { throw new ArcanistUsageException(pht('Aborted generation of gigantic diff.')); } } } } $try_encoding = nonempty($this->getArgument('encoding'), null); $utf8_problems = array(); foreach ($changes as $change) { foreach ($change->getHunks() as $hunk) { $corpus = $hunk->getCorpus(); if (!phutil_is_utf8($corpus)) { // If this corpus is heuristically binary, don't try to convert it. // mb_check_encoding() and mb_convert_encoding() are both very very // liberal about what they're willing to process. $is_binary = ArcanistDiffUtils::isHeuristicBinaryFile($corpus); if (!$is_binary) { if (!$try_encoding) { try { $try_encoding = $this->getRepositoryEncoding(); } catch (ConduitClientException $e) { if ($e->getErrorCode() == 'ERR-BAD-ARCANIST-PROJECT') { echo phutil_console_wrap(pht('Lookup of encoding in arcanist project failed: %s', $e->getMessage()) . "\n"); } else { throw $e; } } } if ($try_encoding) { $corpus = phutil_utf8_convert($corpus, 'UTF-8', $try_encoding); $name = $change->getCurrentPath(); if (phutil_is_utf8($corpus)) { $this->writeStatusMessage(pht("Converted a '%s' hunk from '%s' to UTF-8.\n", $name, $try_encoding)); $hunk->setCorpus($corpus); continue; } } } $utf8_problems[] = $change; break; } } } // If there are non-binary files which aren't valid UTF-8, warn the user // and treat them as binary changes. See D327 for discussion of why Arcanist // has this behavior. if ($utf8_problems) { $utf8_warning = sprintf("%s\n\n%s\n\n %s\n", pht('This diff includes %s file(s) which are not valid UTF-8 (they ' . 'contain invalid byte sequences). You can either stop this ' . 'workflow and fix these files, or continue. If you continue, ' . 'these files will be marked as binary.', phutil_count($utf8_problems)), pht("You can learn more about how Phabricator handles character " . "encodings (and how to configure encoding settings and detect and " . "correct encoding problems) by reading 'User Guide: UTF-8 and " . "Character Encoding' in the Phabricator documentation."), pht('%s AFFECTED FILE(S)', phutil_count($utf8_problems))); $confirm = pht('Do you want to mark these %s file(s) as binary and continue?', phutil_count($utf8_problems)); echo phutil_console_format("**%s**\n", pht('Invalid Content Encoding (Non-UTF8)')); echo phutil_console_wrap($utf8_warning); $file_list = mpull($utf8_problems, 'getCurrentPath'); $file_list = ' ' . implode("\n ", $file_list); echo $file_list; if (!phutil_console_confirm($confirm, $default_no = false)) { throw new ArcanistUsageException(pht('Aborted workflow to fix UTF-8.')); } else { foreach ($utf8_problems as $change) { $change->convertToBinaryChange($repository_api); } } } $this->uploadFilesForChanges($changes); return $changes; }
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); }
private final function doAdjustSchemata($unsafe) { $console = PhutilConsole::getConsole(); $console->writeOut("%s\n", pht('Verifying database schemata...')); list($adjustments, $errors) = $this->findAdjustments(); $api = $this->getAPI(); if (!$adjustments) { $console->writeOut("%s\n", pht('Found no adjustments for schemata.')); return $this->printErrors($errors, 0); } if (!$this->force && !$api->isCharacterSetAvailable('utf8mb4')) { $message = pht("You have an old version of MySQL (older than 5.5) which does not " . "support the utf8mb4 character set. We strongly recomend upgrading to " . "5.5 or newer.\n\n" . "If you apply adjustments now and later update MySQL to 5.5 or newer, " . "you'll need to apply adjustments again (and they will take a long " . "time).\n\n" . "You can exit this workflow, update MySQL now, and then run this " . "workflow again. This is recommended, but may cause a lot of downtime " . "right now.\n\n" . "You can exit this workflow, continue using Phabricator without " . "applying adjustments, update MySQL at a later date, and then run " . "this workflow again. This is also a good approach, and will let you " . "delay downtime until later.\n\n" . "You can proceed with this workflow, and then optionally update " . "MySQL at a later date. After you do, you'll need to apply " . "adjustments again.\n\n" . "For more information, see \"Managing Storage Adjustments\" in " . "the documentation."); $console->writeOut("\n**<bg:yellow> %s </bg>**\n\n%s\n", pht('OLD MySQL VERSION'), phutil_console_wrap($message)); $prompt = pht('Continue with old MySQL version?'); if (!phutil_console_confirm($prompt, $default_no = true)) { return; } } $table = id(new PhutilConsoleTable())->addColumn('database', array('title' => pht('Database')))->addColumn('table', array('title' => pht('Table')))->addColumn('name', array('title' => pht('Name')))->addColumn('info', array('title' => pht('Issues'))); foreach ($adjustments as $adjust) { $info = array(); foreach ($adjust['issues'] as $issue) { $info[] = PhabricatorConfigStorageSchema::getIssueName($issue); } $table->addRow(array('database' => $adjust['database'], 'table' => idx($adjust, 'table'), 'name' => idx($adjust, 'name'), 'info' => implode(', ', $info))); } $console->writeOut("\n\n"); $table->draw(); if ($this->dryRun) { $console->writeOut("%s\n", pht('DRYRUN: Would apply adjustments.')); return 0; } else { if (!$this->force) { $console->writeOut("\n%s\n", pht("Found %s issues(s) with schemata, detailed above.\n\n" . "You can review issues in more detail from the web interface, " . "in Config > Database Status. To better understand the adjustment " . "workflow, see \"Managing Storage Adjustments\" in the " . "documentation.\n\n" . "MySQL needs to copy table data to make some adjustments, so these " . "migrations may take some time.", phutil_count($adjustments))); $prompt = pht('Fix these schema issues?'); if (!phutil_console_confirm($prompt, $default_no = true)) { return 1; } } } $console->writeOut("%s\n", pht('Fixing schema issues...')); $conn = $api->getConn(null); if ($unsafe) { queryfx($conn, 'SET SESSION sql_mode = %s', ''); } else { queryfx($conn, 'SET SESSION sql_mode = %s', 'STRICT_ALL_TABLES'); } $failed = array(); // We make changes in several phases. $phases = array('drop_auto', 'drop_keys', 'main', 'add_keys', 'add_auto'); $bar = id(new PhutilConsoleProgressBar())->setTotal(count($adjustments) * count($phases)); foreach ($phases as $phase) { foreach ($adjustments as $adjust) { try { switch ($adjust['kind']) { case 'database': if ($phase == 'main') { queryfx($conn, 'ALTER DATABASE %T CHARACTER SET = %s COLLATE = %s', $adjust['database'], $adjust['charset'], $adjust['collation']); } break; case 'table': if ($phase == 'main') { queryfx($conn, 'ALTER TABLE %T.%T COLLATE = %s', $adjust['database'], $adjust['table'], $adjust['collation']); } break; case 'column': $apply = false; $auto = false; $new_auto = idx($adjust, 'auto'); if ($phase == 'drop_auto') { if ($new_auto === false) { $apply = true; $auto = false; } } else { if ($phase == 'main') { $apply = true; if ($new_auto === false) { $auto = false; } else { $auto = $adjust['is_auto']; } } else { if ($phase == 'add_auto') { if ($new_auto === true) { $apply = true; $auto = true; } } } } if ($apply) { $parts = array(); if ($auto) { $parts[] = qsprintf($conn, 'AUTO_INCREMENT'); } if ($adjust['charset']) { $parts[] = qsprintf($conn, 'CHARACTER SET %Q COLLATE %Q', $adjust['charset'], $adjust['collation']); } queryfx($conn, 'ALTER TABLE %T.%T MODIFY %T %Q %Q %Q', $adjust['database'], $adjust['table'], $adjust['name'], $adjust['type'], implode(' ', $parts), $adjust['nullable'] ? 'NULL' : 'NOT NULL'); } break; case 'key': if ($phase == 'drop_keys' && $adjust['exists']) { if ($adjust['name'] == 'PRIMARY') { $key_name = 'PRIMARY KEY'; } else { $key_name = qsprintf($conn, 'KEY %T', $adjust['name']); } queryfx($conn, 'ALTER TABLE %T.%T DROP %Q', $adjust['database'], $adjust['table'], $key_name); } if ($phase == 'add_keys' && $adjust['keep']) { // Different keys need different creation syntax. Notable // special cases are primary keys and fulltext keys. if ($adjust['name'] == 'PRIMARY') { $key_name = 'PRIMARY KEY'; } else { if ($adjust['indexType'] == 'FULLTEXT') { $key_name = qsprintf($conn, 'FULLTEXT %T', $adjust['name']); } else { if ($adjust['unique']) { $key_name = qsprintf($conn, 'UNIQUE KEY %T', $adjust['name']); } else { $key_name = qsprintf($conn, '/* NONUNIQUE */ KEY %T', $adjust['name']); } } } queryfx($conn, 'ALTER TABLE %T.%T ADD %Q (%Q)', $adjust['database'], $adjust['table'], $key_name, implode(', ', $adjust['columns'])); } break; default: throw new Exception(pht('Unknown schema adjustment kind "%s"!', $adjust['kind'])); } } catch (AphrontQueryException $ex) { $failed[] = array($adjust, $ex); } $bar->update(1); } } $bar->done(); if (!$failed) { $console->writeOut("%s\n", pht('Completed fixing all schema issues.')); $err = 0; } else { $table = id(new PhutilConsoleTable())->addColumn('target', array('title' => pht('Target')))->addColumn('error', array('title' => pht('Error'))); foreach ($failed as $failure) { list($adjust, $ex) = $failure; $pieces = array_select_keys($adjust, array('database', 'table', 'name')); $pieces = array_filter($pieces); $target = implode('.', $pieces); $table->addRow(array('target' => $target, 'error' => $ex->getMessage())); } $console->writeOut("\n"); $table->draw(); $console->writeOut("\n%s\n", pht('Failed to make some schema adjustments, detailed above.')); $console->writeOut("%s\n", pht('For help troubleshooting adjustments, see "Managing Storage ' . 'Adjustments" in the documentation.')); $err = 1; } return $this->printErrors($errors, $err); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $viewer = $this->getViewer(); $query = id(new PhabricatorExternalAccountQuery())->setViewer($viewer)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT)); $username = $args->getArg('user'); if (strlen($username)) { $user = id(new PhabricatorPeopleQuery())->setViewer($viewer)->withUsernames(array($username))->executeOne(); if ($user) { $query->withUserPHIDs(array($user->getPHID())); } else { throw new PhutilArgumentUsageException(pht('No such user "%s"!', $username)); } } $type = $args->getArg('type'); if (strlen($type)) { $query->withAccountTypes(array($type)); } $domain = $args->getArg('domain'); if (strlen($domain)) { $query->withAccountDomains(array($domain)); } $accounts = $query->execute(); if (!$accounts) { throw new PhutilArgumentUsageException(pht('No accounts match the arguments!')); } else { $console->writeOut("%s\n", pht('Found %s account(s) to refresh.', phutil_count($accounts))); } $providers = PhabricatorAuthProvider::getAllEnabledProviders(); foreach ($accounts as $account) { $console->writeOut("%s\n", pht('Refreshing account #%d (%s/%s).', $account->getID(), $account->getAccountType(), $account->getAccountDomain())); $key = $account->getProviderKey(); if (empty($providers[$key])) { $console->writeOut("> %s\n", pht('Skipping, provider is not enabled or does not exist.')); continue; } $provider = $providers[$key]; if (!$provider instanceof PhabricatorOAuth2AuthProvider) { $console->writeOut("> %s\n", pht('Skipping, provider is not an OAuth2 provider.')); continue; } $adapter = $provider->getAdapter(); if (!$adapter->supportsTokenRefresh()) { $console->writeOut("> %s\n", pht('Skipping, provider does not support token refresh.')); continue; } $refresh_token = $account->getProperty('oauth.token.refresh'); if (!$refresh_token) { $console->writeOut("> %s\n", pht('Skipping, provider has no stored refresh token.')); continue; } $console->writeOut("+ %s\n", pht('Refreshing token, current token expires in %s seconds.', new PhutilNumber($account->getProperty('oauth.token.access.expires') - time()))); $token = $provider->getOAuthAccessToken($account, $force_refresh = true); if (!$token) { $console->writeOut("* %s\n", pht('Unable to refresh token!')); continue; } $console->writeOut("+ %s\n", pht('Refreshed token, new token expires in %s seconds.', new PhutilNumber($account->getProperty('oauth.token.access.expires') - time()))); } $console->writeOut("%s\n", pht('Done.')); return 0; }