private function saveConfig() { $config = $this->getSource()->getAllKeys(); $json = new PhutilJSON(); $data = $json->encodeFormatted($config); Filesystem::writeFile($this->getConfigPath(), $data); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $argv = $args->getArg('args'); if (count($argv) == 0) { throw new PhutilArgumentUsageException('Specify a configuration key to get.'); } $key = $argv[0]; if (count($argv) > 1) { throw new PhutilArgumentUsageException('Too many arguments: expected one key.'); } $options = PhabricatorApplicationConfigOptions::loadAllOptions(); if (empty($options[$key])) { throw new PhutilArgumentUsageException("No such configuration key '{$key}'! Use `config list` to list all " . "keys."); } $config = new PhabricatorConfigLocalSource(); $values = $config->getKeys(array($key)); $result = array(); foreach ($values as $key => $value) { $result[] = array('key' => $key, 'source' => 'local', 'value' => $value); } $result = array('config' => $result); $json = new PhutilJSON(); $console->writeOut($json->encodeFormatted($result)); }
public function getOptions() { $policy_locked_type = 'custom:PolicyLockOptionType'; $policy_locked_example = array('people.create.users' => 'admin'); $json = new PhutilJSON(); $policy_locked_example = $json->encodeFormatted($policy_locked_example); return array($this->newOption('policy.allow-public', 'bool', false)->setBoolOptions(array(pht('Allow Public Visibility'), pht('Require Login')))->setSummary(pht('Allow users to set object visibility to public.'))->setDescription(pht("Phabricator allows you to set the visibility of objects (like " . "repositories and tasks) to 'Public', which means **anyone " . "on the internet can see them, without needing to log in or " . "have an account**." . "\n\n" . "This is intended for open source projects. Many installs will " . "never want to make anything public, so this policy is disabled " . "by default. You can enable it here, which will let you set the " . "policy for objects to 'Public'." . "\n\n" . "Enabling this setting will immediately open up some features, " . "like the user directory. Anyone on the internet will be able to " . "access these features." . "\n\n" . "With this setting disabled, the 'Public' policy is not " . "available, and the most open policy is 'All Users' (which means " . "users must have accounts and be logged in to view things).")), $this->newOption('policy.locked', $policy_locked_type, array())->setLocked(true)->setSummary(pht('Lock specific application policies so they can not be edited.'))->setDescription(pht('Phabricator has application policies which can dictate whether ' . 'users can take certain actions, such as creating new users. ' . "\n\n" . 'This setting allows for "locking" these policies such that no ' . 'further edits can be made on a per-policy basis.'))->addExample($policy_locked_example, pht('Lock Create User Policy To Admins'))); }
public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $resource = new DrydockResource(); $json = new PhutilJSON(); $err_attributes = true; $err_capabilities = true; $json_attributes = $json->encodeFormatted($resource->getAttributes()); $json_capabilities = $json->encodeFormatted($resource->getCapabilities()); $errors = array(); if ($request->isFormPost()) { $raw_attributes = $request->getStr('attributes'); $attributes = json_decode($raw_attributes, true); if (!is_array($attributes)) { $err_attributes = 'Invalid'; $errors[] = 'Enter attributes as a valid JSON object.'; $json_attributes = $raw_attributes; } else { $resource->setAttributes($attributes); $json_attributes = $json->encodeFormatted($attributes); $err_attributes = null; } $raw_capabilities = $request->getStr('capabilities'); $capabilities = json_decode($raw_capabilities, true); if (!is_array($capabilities)) { $err_capabilities = 'Invalid'; $errors[] = 'Enter capabilities as a valid JSON object.'; $json_capabilities = $raw_capabilities; } else { $resource->setCapabilities($capabilities); $json_capabilities = $json->encodeFormatted($capabilities); $err_capabilities = null; } $resource->setBlueprintClass($request->getStr('blueprint')); $resource->setType($resource->getBlueprint()->getType()); $resource->setOwnerPHID($user->getPHID()); $resource->setName($request->getStr('name')); if (!$errors) { $resource->save(); return id(new AphrontRedirectResponse())->setURI('/drydock/resource/'); } } $error_view = null; if ($errors) { $error_view = new AphrontErrorView(); $error_view->setTitle('Form Errors'); $error_view->setErrors($errors); } $blueprints = id(new PhutilSymbolLoader())->setType('class')->setAncestorClass('DrydockBlueprint')->selectAndLoadSymbols(); $blueprints = ipull($blueprints, 'name', 'name'); $panel = new AphrontPanelView(); $panel->setWidth(AphrontPanelView::WIDTH_FORM); $panel->setHeader('Allocate Drydock Resource'); $form = id(new AphrontFormView())->setUser($request->getUser())->appendChild(id(new AphrontFormTextControl())->setLabel('Name')->setName('name')->setValue($resource->getName()))->appendChild(id(new AphrontFormSelectControl())->setLabel('Blueprint')->setOptions($blueprints)->setName('blueprint')->setValue($resource->getBlueprintClass()))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Attributes')->setName('attributes')->setValue($json_attributes)->setError($err_attributes)->setCaption('Specify attributes in JSON.'))->appendChild(id(new AphrontFormTextAreaControl())->setLabel('Capabilities')->setName('capabilities')->setValue($json_capabilities)->setError($err_capabilities)->setCaption('Specify capabilities in JSON.'))->appendChild(id(new AphrontFormSubmitControl())->setValue('Allocate Resource')); $panel->appendChild($form); return $this->buildStandardPageResponse(array($error_view, $panel), array('title' => 'Allocate Resource')); }
public function getDisplayValue(PhabricatorConfigOption $option, PhabricatorConfigEntry $entry, $value) { if (is_array($value)) { $json = new PhutilJSON(); return $json->encodeFormatted($value); } else { return $value; } }
public function testEmptyArrayEncoding() { $serializer = new PhutilJSON(); $expect = <<<EOJSON { "x" : [] } EOJSON; $this->assertEqual($expect, $serializer->encodeFormatted(array('x' => array())), 'Empty arrays should serialize as [], not {}.'); }
public function getMethodDescription() { $types = HarbormasterArtifact::getAllArtifactTypes(); $types = msort($types, 'getArtifactTypeName'); $head_key = pht('Key'); $head_type = pht('Type'); $head_desc = pht('Description'); $head_atype = pht('Artifact Type'); $head_name = pht('Name'); $head_summary = pht('Summary'); $out = array(); $out[] = pht('Use this method to attach artifacts to build targets while running ' . 'builds. Artifacts can be used to carry data through a complex build ' . 'workflow, provide extra information to users, or store build results.'); $out[] = null; $out[] = pht('When creating an artifact, you will choose an `artifactType` from ' . 'this table. These types of artifacts are supported:'); $out[] = "| {$head_atype} | {$head_name} | {$head_summary} |"; $out[] = '|-------------|--------------|--------------|'; foreach ($types as $type) { $type_name = $type->getArtifactTypeName(); $type_const = $type->getArtifactConstant(); $type_summary = $type->getArtifactTypeSummary(); $out[] = "| `{$type_const}` | **{$type_name}** | {$type_summary} |"; } $out[] = null; $out[] = pht('Each artifact also needs an `artifactKey`, which names the artifact. ' . 'Finally, you will provide some `artifactData` to fill in the content ' . 'of the artifact. The data you provide depends on what type of artifact ' . 'you are creating.'); foreach ($types as $type) { $type_name = $type->getArtifactTypeName(); $type_const = $type->getArtifactConstant(); $out[] = $type_name; $out[] = '--------------------------'; $out[] = null; $out[] = $type->getArtifactTypeDescription(); $out[] = null; $out[] = pht('Create an artifact of this type by passing `%s` as the ' . '`artifactType`. When creating an artifact of this type, provide ' . 'these parameters as a dictionary to `artifactData`:', $type_const); $spec = $type->getArtifactParameterSpecification(); $desc = $type->getArtifactParameterDescriptions(); $out[] = "| {$head_key} | {$head_type} | {$head_desc} |"; $out[] = '|-------------|--------------|--------------|'; foreach ($spec as $key => $key_type) { $key_desc = idx($desc, $key); $out[] = "| `{$key}` | //{$key_type}// | {$key_desc} |"; } $example = $type->getArtifactDataExample(); if ($example !== null) { $json = new PhutilJSON(); $rendered = $json->encodeFormatted($example); $out[] = pht('For example:'); $out[] = '```lang=json'; $out[] = $rendered; $out[] = '```'; } } return implode("\n", $out); }
public function execute(PhutilArgumentParser $args) { $console = PhutilConsole::getConsole(); $argv = $args->getArg('args'); if (count($argv) == 0) { throw new PhutilArgumentUsageException(pht('Specify a configuration key to get.')); } $key = $argv[0]; if (count($argv) > 1) { throw new PhutilArgumentUsageException(pht('Too many arguments: expected one key.')); } $options = PhabricatorApplicationConfigOptions::loadAllOptions(); if (empty($options[$key])) { throw new PhutilArgumentUsageException(pht("No such configuration key '%s'! Use `%s` to list all keys.", $key, 'config list')); } $values = array(); $config = new PhabricatorConfigLocalSource(); $local_value = $config->getKeys(array($key)); if (empty($local_value)) { $values['local'] = array('key' => $key, 'value' => null, 'status' => 'unset', 'errorInfo' => null); } else { $values['local'] = array('key' => $key, 'value' => reset($local_value), 'status' => 'set', 'errorInfo' => null); } try { $database_config = new PhabricatorConfigDatabaseSource('default'); $database_value = $database_config->getKeys(array($key)); if (empty($database_value)) { $values['database'] = array('key' => $key, 'value' => null, 'status' => 'unset', 'errorInfo' => null); } else { $values['database'] = array('key' => $key, 'value' => reset($database_value), 'status' => 'set', 'errorInfo' => null); } } catch (Exception $e) { $values['database'] = array('key' => $key, 'value' => null, 'status' => 'error', 'errorInfo' => pht('Database source is not configured properly')); } $result = array(); foreach ($values as $source => $value) { $result[] = array('key' => $value['key'], 'source' => $source, 'value' => $value['value'], 'status' => $value['status'], 'errorInfo' => $value['errorInfo']); } $result = array('config' => $result); $json = new PhutilJSON(); $console->writeOut($json->encodeFormatted($result)); }
public function run() { $working_copy = $this->getWorkingCopy(); $paths = $this->getArgument('paths'); $rev = $this->getArgument('rev'); $everything = $this->getArgument('everything'); if ($everything && $paths) { throw new ArcanistUsageException(pht('You can not specify paths with %s. The %s flag runs every test.', '--everything', '--everything')); } if ($everything) { $paths = iterator_to_array($this->getRepositoryApi()->getAllFiles()); } else { $paths = $this->selectPathsForWorkflow($paths, $rev); } $this->engine = $this->newUnitTestEngine($this->getArgument('engine')); if ($everything) { $this->engine->setRunAllTests(true); } else { $this->engine->setPaths($paths); } $this->engine->setArguments($this->getPassthruArgumentsAsMap('unit')); $renderer = new ArcanistUnitConsoleRenderer(); $this->engine->setRenderer($renderer); $enable_coverage = null; // Means "default". if ($this->getArgument('coverage') || $this->getArgument('detailed-coverage')) { $enable_coverage = true; } else { if ($this->getArgument('no-coverage')) { $enable_coverage = false; } } $this->engine->setEnableCoverage($enable_coverage); // Enable possible async tests only for 'arc diff' not 'arc unit' if ($this->getParentWorkflow()) { $this->engine->setEnableAsyncTests(true); } else { $this->engine->setEnableAsyncTests(false); } $results = $this->engine->run(); $this->validateUnitEngineResults($this->engine, $results); $this->testResults = $results; $console = PhutilConsole::getConsole(); $output_format = $this->getOutputFormat(); if ($output_format !== 'full') { $console->disableOut(); } $unresolved = array(); $coverage = array(); foreach ($results as $result) { $result_code = $result->getResult(); if ($this->engine->shouldEchoTestResults()) { $console->writeOut('%s', $renderer->renderUnitResult($result)); } if ($result_code != ArcanistUnitTestResult::RESULT_PASS) { $unresolved[] = $result; } if ($result->getCoverage()) { foreach ($result->getCoverage() as $file => $report) { $coverage[$file][] = $report; } } } if ($coverage) { $file_coverage = array_fill_keys($paths, 0); $file_reports = array(); foreach ($coverage as $file => $reports) { $report = ArcanistUnitTestResult::mergeCoverage($reports); $cov = substr_count($report, 'C'); $uncov = substr_count($report, 'U'); if ($cov + $uncov) { $coverage = $cov / ($cov + $uncov); } else { $coverage = 0; } $file_coverage[$file] = $coverage; $file_reports[$file] = $report; } $console->writeOut("\n__%s__\n", pht('COVERAGE REPORT')); asort($file_coverage); foreach ($file_coverage as $file => $coverage) { $console->writeOut(" **%s%%** %s\n", sprintf('% 3d', (int) (100 * $coverage)), $file); $full_path = $working_copy->getProjectRoot() . '/' . $file; if ($this->getArgument('detailed-coverage') && Filesystem::pathExists($full_path) && is_file($full_path) && array_key_exists($file, $file_reports)) { $console->writeOut('%s', $this->renderDetailedCoverageReport(Filesystem::readFile($full_path), $file_reports[$file])); } } } $this->unresolvedTests = $unresolved; $overall_result = self::RESULT_OKAY; foreach ($results as $result) { $result_code = $result->getResult(); if ($result_code == ArcanistUnitTestResult::RESULT_FAIL || $result_code == ArcanistUnitTestResult::RESULT_BROKEN) { $overall_result = self::RESULT_FAIL; break; } else { if ($result_code == ArcanistUnitTestResult::RESULT_UNSOUND) { $overall_result = self::RESULT_UNSOUND; } } } if ($output_format !== 'full') { $console->enableOut(); } $data = array_values(mpull($results, 'toDictionary')); switch ($output_format) { case 'ugly': $console->writeOut('%s', json_encode($data)); break; case 'json': $json = new PhutilJSON(); $console->writeOut('%s', $json->encodeFormatted($data)); break; case 'full': // already printed break; case 'none': // do nothing break; } return $overall_result; }
private function executeParserTest($name, $file) { $contents = Filesystem::readFile($file); $contents = preg_split('/^~{4,}\\n/m', $contents); if (count($contents) < 2) { throw new Exception(pht("Expected '%s' separating test case and results.", '~~~~~~~~~~')); } list($data, $options, $expect) = array_merge($contents, array(null)); $options = id(new PhutilSimpleOptions())->parse($options); $type = null; foreach ($options as $key => $value) { switch ($key) { case 'pass': case 'fail-syntax': case 'fail-parse': if ($type !== null) { throw new Exception(pht('Test file "%s" unexpectedly specifies multiple expected ' . 'test outcomes.', $name)); } $type = $key; break; case 'comment': // Human readable comment providing test case information. break; case 'rtrim': // Allows construction of tests which rely on EOF without newlines. $data = rtrim($data); break; default: throw new Exception(pht('Test file "%s" has unknown option "%s" in its options ' . 'string.', $name, $key)); } } if ($type === null) { throw new Exception(pht('Test file "%s" does not specify a test result (like "pass") in ' . 'its options string.', $name)); } $future = PhutilXHPASTBinary::getParserFuture($data); list($err, $stdout, $stderr) = $future->resolve(); switch ($type) { case 'pass': case 'fail-parse': $this->assertEqual(0, $err, pht('Exit code for "%s".', $name)); if (!strlen($expect)) { // If there's no "expect" data in the test case, that's OK. break; } try { $expect = phutil_json_decode($expect); } catch (PhutilJSONParserException $ex) { throw new PhutilProxyException(pht('Expect data for test "%s" is not valid JSON.', $name), $ex); } try { $stdout = phutil_json_decode($stdout); } catch (PhutilJSONParserException $ex) { throw new PhutilProxyException(pht('Output for test file "%s" is not valid JSON.', $name), $ex); } $json = new PhutilJSON(); $expect_nice = $json->encodeFormatted($expect); $stdout_nice = $json->encodeFormatted($stdout); if ($type == 'pass') { $this->assertEqual($expect_nice, $stdout_nice, pht('Parser output for "%s".', $name)); } else { $this->assertFalse($expect_nice == $stdout_nice, pht('Expected parser to parse "%s" incorrectly.', $name)); } break; case 'fail-syntax': $this->assertEqual(1, $err, pht('Exit code for "%s".', $name)); $this->assertTrue((bool) preg_match('/syntax error/', $stderr), pht('Expect "syntax error" in stderr or "%s".', $name)); break; } }
/** * This is probably not the method you're looking for; try * @{method:writeUserArcConfig}. */ public function writeUserConfigurationFile($config) { $json_encoder = new PhutilJSON(); $json = $json_encoder->encodeFormatted($config); $path = $this->getUserConfigurationFileLocation(); Filesystem::writeFile($path, $json); if (!phutil_is_windows()) { execx('chmod 600 %s', $path); } }
public function getOptions() { $priority_defaults = array(100 => array('name' => pht('Unbreak Now!'), 'short' => pht('Unbreak!'), 'color' => 'indigo'), 90 => array('name' => pht('Needs Triage'), 'short' => pht('Triage'), 'color' => 'violet'), 80 => array('name' => pht('High'), 'short' => pht('High'), 'color' => 'red'), 50 => array('name' => pht('Normal'), 'short' => pht('Normal'), 'color' => 'orange'), 25 => array('name' => pht('Low'), 'short' => pht('Low'), 'color' => 'yellow'), 0 => array('name' => pht('Wishlist'), 'short' => pht('Wish'), 'color' => 'sky')); $status_type = 'custom:ManiphestStatusConfigOptionType'; $status_defaults = array('open' => array('name' => pht('Open'), 'special' => ManiphestTaskStatus::SPECIAL_DEFAULT), 'resolved' => array('name' => pht('Resolved'), 'name.full' => pht('Closed, Resolved'), 'closed' => true, 'special' => ManiphestTaskStatus::SPECIAL_CLOSED, 'prefixes' => array('closed', 'closes', 'close', 'fix', 'fixes', 'fixed', 'resolve', 'resolves', 'resolved'), 'suffixes' => array('as resolved', 'as fixed')), 'wontfix' => array('name' => pht('Wontfix'), 'name.full' => pht('Closed, Wontfix'), 'closed' => true, 'prefixes' => array('wontfix', 'wontfixes', 'wontfixed'), 'suffixes' => array('as wontfix')), 'invalid' => array('name' => pht('Invalid'), 'name.full' => pht('Closed, Invalid'), 'closed' => true, 'prefixes' => array('invalidate', 'invalidates', 'invalidated'), 'suffixes' => array('as invalid')), 'duplicate' => array('name' => pht('Duplicate'), 'name.full' => pht('Closed, Duplicate'), 'transaction.icon' => 'fa-times', 'special' => ManiphestTaskStatus::SPECIAL_DUPLICATE, 'closed' => true), 'spite' => array('name' => pht('Spite'), 'name.full' => pht('Closed, Spite'), 'name.action' => pht('Spited'), 'transaction.icon' => 'fa-thumbs-o-down', 'silly' => true, 'closed' => true, 'prefixes' => array('spite', 'spites', 'spited'), 'suffixes' => array('out of spite', 'as spite'))); $status_description = $this->deformat(pht(<<<EOTEXT Allows you to edit, add, or remove the task statuses available in Maniphest, like "Open", "Resolved" and "Invalid". The configuration should contain a map of status constants to status specifications (see defaults below for examples). The constant for each status should be 1-12 characters long and contain only lowercase letters and digits. Valid examples are "open", "closed", and "invalid". Users will not normally see these values. The keys you can provide in a specification are: - `name` //Required string.// Name of the status, like "Invalid". - `name.full` //Optional string.// Longer name, like "Closed, Invalid". This appears on the task detail view in the header. - `name.action` //Optional string.// Action name for email subjects, like "Marked Invalid". - `closed` //Optional bool.// Statuses are either "open" or "closed". Specifying `true` here will mark the status as closed (like "Resolved" or "Invalid"). By default, statuses are open. - `special` //Optional string.// Mark this status as special. The special statuses are: - `default` This is the default status for newly created tasks. You must designate one status as default, and it must be an open status. - `closed` This is the default status for closed tasks (for example, tasks closed via the "!close" action in email or via the quick close button in Maniphest). You must designate one status as the default closed status, and it must be a closed status. - `duplicate` This is the status used when tasks are merged into one another as duplicates. You must designate one status for duplicates, and it must be a closed status. - `transaction.icon` //Optional string.// Allows you to choose a different icon to use for this status when showing status changes in the transaction log. Please see UIExamples, Icons and Images for a list. - `transaction.color` //Optional string.// Allows you to choose a different color to use for this status when showing status changes in the transaction log. - `silly` //Optional bool.// Marks this status as silly, and thus wholly inappropriate for use by serious businesses. - `prefixes` //Optional list<string>.// Allows you to specify a list of text prefixes which will trigger a task transition into this status when mentioned in a commit message. For example, providing "closes" here will allow users to move tasks to this status by writing `Closes T123` in commit messages. - `suffixes` //Optional list<string>.// Allows you to specify a list of text suffixes which will trigger a task transition into this status when mentioned in a commit message, after a valid prefix. For example, providing "as invalid" here will allow users to move tasks to this status by writing `Closes T123 as invalid`, even if another status is selected by the "Closes" prefix. Examining the default configuration and examples below will probably be helpful in understanding these options. EOTEXT )); $status_example = array('open' => array('name' => 'Open', 'special' => 'default'), 'closed' => array('name' => 'Closed', 'special' => 'closed', 'closed' => true), 'duplicate' => array('name' => 'Duplicate', 'special' => 'duplicate', 'closed' => true)); $json = new PhutilJSON(); $status_example = $json->encodeFormatted($status_example); // This is intentionally blank for now, until we can move more Maniphest // logic to custom fields. $default_fields = array(); foreach ($default_fields as $key => $enabled) { $default_fields[$key] = array('disabled' => !$enabled); } $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; return array($this->newOption('maniphest.custom-field-definitions', 'wild', array())->setSummary(pht('Custom Maniphest fields.'))->setDescription(pht('Array of custom fields for Maniphest tasks. For details on ' . 'adding custom fields to Maniphest, see "Configuring Custom ' . 'Fields" in the documentation.'))->addExample('{"mycompany:estimated-hours": {"name": "Estimated Hours", ' . '"type": "int", "caption": "Estimated number of hours this will ' . 'take."}}', pht('Valid Setting')), $this->newOption('maniphest.fields', $custom_field_type, $default_fields)->setCustomData(id(new ManiphestTask())->getCustomFieldBaseClass())->setDescription(pht('Select and reorder task fields.')), $this->newOption('maniphest.priorities', 'wild', $priority_defaults)->setSummary(pht('Configure Maniphest priority names.'))->setDescription(pht('Allows you to edit or override the default priorities available ' . 'in Maniphest, like "High", "Normal" and "Low". The configuration ' . 'should contain a map of priority constants to priority ' . 'specifications (see defaults below for examples).' . "\n\n" . 'The keys you can define for a priority are:' . "\n\n" . ' - `name` Name of the priority.' . "\n" . ' - `short` Alternate shorter name, used in UIs where there is ' . ' not much space available.' . "\n" . ' - `color` A color for this priority, like "red" or "blue".' . "\n\n" . 'You can choose which priority is the default for newly created ' . 'tasks with `maniphest.default-priority`.')), $this->newOption('maniphest.statuses', $status_type, $status_defaults)->setSummary(pht('Configure Maniphest task statuses.'))->setDescription($status_description)->addExample($status_example, pht('Minimal Valid Config')), $this->newOption('maniphest.default-priority', 'int', 90)->setSummary(pht('Default task priority for create flows.'))->setDescription(pht("What should the default task priority be in create flows? See " . "the constants in @{class:ManiphestTaskPriority} for valid " . "values. Defaults to 'needs triage'.")), $this->newOption('metamta.maniphest.reply-handler-domain', 'string', null)->setSummary(pht('Enable replying to tasks via email.'))->setDescription(pht('You can configure a reply handler domain so that email sent from ' . 'Maniphest will have a special "Reply To" address like ' . '"*****@*****.**" that allows recipients to reply by ' . 'email and interact with tasks. For instructions on configurating ' . 'reply handlers, see the article "Configuring Inbound Email" in ' . 'the Phabricator documentation. By default, this is set to `null` ' . 'and Phabricator will use a generic `noreply@` address or the ' . 'address of the acting user instead of a special reply handler ' . 'address (see `metamta.default-address`). If you set a domain ' . 'here, Phabricator will begin generating private reply handler ' . 'addresses. See also `metamta.maniphest.reply-handler` to further ' . 'configure behavior. This key should be set to the domain part ' . 'after the @, like "example.com".')), $this->newOption('metamta.maniphest.reply-handler', 'class', 'ManiphestReplyHandler')->setBaseClass('PhabricatorMailReplyHandler')->setDescription(pht('Override reply handler class.')), $this->newOption('metamta.maniphest.subject-prefix', 'string', '[Maniphest]')->setDescription(pht('Subject prefix for Maniphest mail.')), $this->newOption('metamta.maniphest.public-create-email', 'string', null)->setSummary(pht('Allow filing bugs via email.'))->setDescription(pht('You can configure an email address like ' . '"*****@*****.**" which will automatically create ' . 'Maniphest tasks when users send email to it. This relies on the ' . '"From" address to authenticate users, so it is is not completely ' . 'secure. To set this up, enter a complete email address like ' . '"*****@*****.**" and then configure mail to that ' . 'address so it routed to Phabricator (if you\'ve already ' . 'configured reply handlers, you\'re probably already done). See ' . '"Configuring Inbound Email" in the documentation for more ' . 'information.')), $this->newOption('metamta.maniphest.default-public-author', 'string', null)->setSummary(pht('Username anonymous bugs are filed under.'))->setDescription(pht('If you enable `metamta.maniphest.public-create-email` and create ' . 'an email address like "*****@*****.**", it will ' . 'default to rejecting mail which doesn\'t come from a known user. ' . 'However, you might want to let anyone send email to this ' . 'address; to do so, set a default author here (a Phabricator ' . 'username). A typical use of this might be to create a "System ' . 'Agent" user called "bugs" and use that name here. If you specify ' . 'a valid username, mail will always be accepted and used to ' . 'create a task, even if the sender is not a system user. The ' . 'original email address will be stored in an `From Email` field ' . 'on the task.')), $this->newOption('maniphest.priorities.unbreak-now', 'int', 100)->setSummary(pht('Priority used to populate "Unbreak Now" on home.'))->setDescription(pht('Temporary setting. If set, this priority is used to populate the ' . '"Unbreak Now" panel on the home page. You should adjust this if ' . 'you adjust priorities using `maniphest.priorities`.')), $this->newOption('maniphest.priorities.needs-triage', 'int', 90)->setSummary(pht('Priority used to populate "Needs Triage" on home.'))->setDescription(pht('Temporary setting. If set, this priority is used to populate the ' . '"Needs Triage" panel on the home page. You should adjust this if ' . 'you adjust priorities using `maniphest.priorities`.'))); }
public function generateManifest($path) { $data = $this->buildManifest(); $json = new PhutilJSON(); $data = $json->encodeFormatted($data); Filesystem::writeFile($path, $data); return $this; }
public function writeLocalArcConfig(array $config) { $json_encoder = new PhutilJSON(); $json = $json_encoder->encodeFormatted($config); $this->writeScratchFile('config', $json); return $this; }
/** * Build or rebuild the library map. * * @return this * * @task map */ public function buildMap() { // Identify all the ".php" source files in the library. $this->log("Finding source files...\n"); $source_map = $this->loadSourceFileMap(); $this->log("Found " . number_format(count($source_map)) . " files.\n"); // Load the symbol cache with existing parsed symbols. This allows us // to remap libraries quickly by analyzing only changed files. $this->log("Loading symbol cache...\n"); $symbol_cache = $this->loadSymbolCache(); // Build out the symbol analysis for all the files in the library. For // each file, check if it's in cache. If we miss in the cache, do a fresh // analysis. $symbol_map = array(); $futures = array(); foreach ($source_map as $file => $hash) { if (!empty($symbol_cache[$hash])) { $symbol_map[$file] = $symbol_cache[$hash]; continue; } $futures[$file] = $this->buildSymbolAnalysisFuture($file); } $this->log("Found " . number_format(count($symbol_map)) . " files in cache.\n"); // Run the analyzer on any files which need analysis. if ($futures) { $limit = $this->subprocessLimit; $count = number_format(count($futures)); $this->log("Analyzing {$count} files with {$limit} subprocesses...\n"); foreach (Futures($futures)->limit($limit) as $file => $future) { $result = $future->resolveJSON(); if (empty($result['error'])) { $symbol_map[$file] = $result; } else { echo phutil_console_format("\n**SYNTAX ERROR!**\nFile: %s\nLine: %d\n\n%s\n", Filesystem::readablePath($result['file']), $result['line'], $result['error']); exit(1); } $this->log("."); } $this->log("\nDone.\n"); } // We're done building the cache, so write it out immediately. Note that // we've only retained entries for files we found, so this implicitly cleans // out old cache entries. $this->writeSymbolCache($symbol_map, $source_map); // Our map is up to date, so either show it on stdout or write it to disk. if ($this->showMap) { $this->log("Showing map...\n"); if ($this->ugly) { echo json_encode($symbol_map); } else { $json = new PhutilJSON(); echo $json->encodeFormatted($symbol_map); } } else { $this->log("Building library map...\n"); $library_map = $this->buildLibraryMap($symbol_map); $this->log("Writing map...\n"); $this->writeLibraryMap($library_map); } $this->log("Done.\n"); return $this; }
public function run() { $working_copy = $this->getWorkingCopy(); $engine_class = $this->getArgument('engine', $working_copy->getConfigFromAnySource('unit.engine')); if (!$engine_class) { throw new ArcanistNoEngineException("No unit test engine is configured for this project. Edit .arcconfig " . "to specify a unit test engine."); } $paths = $this->getArgument('paths'); $rev = $this->getArgument('rev'); $everything = $this->getArgument('everything'); if ($everything && $paths) { throw new ArcanistUsageException("You can not specify paths with --everything. The --everything " . "flag runs every test."); } $paths = $this->selectPathsForWorkflow($paths, $rev); if (!class_exists($engine_class) || !is_subclass_of($engine_class, 'ArcanistBaseUnitTestEngine')) { throw new ArcanistUsageException("Configured unit test engine '{$engine_class}' is not a subclass of " . "'ArcanistBaseUnitTestEngine'."); } $this->engine = newv($engine_class, array()); $this->engine->setWorkingCopy($working_copy); if ($everything) { $this->engine->setRunAllTests(true); } else { $this->engine->setPaths($paths); } $this->engine->setArguments($this->getPassthruArgumentsAsMap('unit')); $renderer = new ArcanistUnitConsoleRenderer(); $this->engine->setRenderer($renderer); $enable_coverage = null; // Means "default". if ($this->getArgument('coverage') || $this->getArgument('detailed-coverage')) { $enable_coverage = true; } else { if ($this->getArgument('no-coverage')) { $enable_coverage = false; } } $this->engine->setEnableCoverage($enable_coverage); // Enable possible async tests only for 'arc diff' not 'arc unit' if ($this->getParentWorkflow()) { $this->engine->setEnableAsyncTests(true); } else { $this->engine->setEnableAsyncTests(false); } $results = $this->engine->run(); $this->testResults = $results; $console = PhutilConsole::getConsole(); $json_output = $this->getArgument('json'); if ($json_output) { $console->disableOut(); } $unresolved = array(); $coverage = array(); $postponed_count = 0; foreach ($results as $result) { $result_code = $result->getResult(); if ($result_code == ArcanistUnitTestResult::RESULT_POSTPONED) { $postponed_count++; $unresolved[] = $result; } else { if ($this->engine->shouldEchoTestResults()) { $console->writeOut('%s', $renderer->renderUnitResult($result)); } if ($result_code != ArcanistUnitTestResult::RESULT_PASS) { $unresolved[] = $result; } } if ($result->getCoverage()) { foreach ($result->getCoverage() as $file => $report) { $coverage[$file][] = $report; } } } if ($postponed_count) { $console->writeOut('%s', $renderer->renderPostponedResult($postponed_count)); } if ($coverage) { $file_coverage = array_fill_keys($paths, 0); $file_reports = array(); foreach ($coverage as $file => $reports) { $report = ArcanistUnitTestResult::mergeCoverage($reports); $cov = substr_count($report, 'C'); $uncov = substr_count($report, 'U'); if ($cov + $uncov) { $coverage = $cov / ($cov + $uncov); } else { $coverage = 0; } $file_coverage[$file] = $coverage; $file_reports[$file] = $report; } $console->writeOut("\n__COVERAGE REPORT__\n"); asort($file_coverage); foreach ($file_coverage as $file => $coverage) { $console->writeOut(" **%s%%** %s\n", sprintf('% 3d', (int) (100 * $coverage)), $file); $full_path = $working_copy->getProjectRoot() . '/' . $file; if ($this->getArgument('detailed-coverage') && Filesystem::pathExists($full_path) && is_file($full_path)) { $console->writeOut('%s', $this->renderDetailedCoverageReport(Filesystem::readFile($full_path), $file_reports[$file])); } } } $this->unresolvedTests = $unresolved; $overall_result = self::RESULT_OKAY; foreach ($results as $result) { $result_code = $result->getResult(); if ($result_code == ArcanistUnitTestResult::RESULT_FAIL || $result_code == ArcanistUnitTestResult::RESULT_BROKEN) { $overall_result = self::RESULT_FAIL; break; } else { if ($result_code == ArcanistUnitTestResult::RESULT_UNSOUND) { $overall_result = self::RESULT_UNSOUND; } else { if ($result_code == ArcanistUnitTestResult::RESULT_POSTPONED && $overall_result != self::RESULT_UNSOUND) { $overall_result = self::RESULT_POSTPONED; } } } } if ($json_output) { $console->enableOut(); $data = array_values(mpull($results, 'toDictionary')); if ($this->getArgument('ugly')) { $console->writeOut('%s', json_encode($data)); } else { $json = new PhutilJSON(); $console->writeOut('%s', $json->encodeFormatted($data)); } } return $overall_result; }
public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $key = $request->getURIData('key'); $is_new = $key === null; if ($is_new) { $var = new PhluxVariable(); $var->setViewPolicy(PhabricatorPolicies::POLICY_USER); $var->setEditPolicy(PhabricatorPolicies::POLICY_USER); } else { $var = id(new PhluxVariableQuery())->setViewer($viewer)->requireCapabilities(array(PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT))->withKeys(array($key))->executeOne(); if (!$var) { return new Aphront404Response(); } $view_uri = $this->getApplicationURI('/view/' . $key . '/'); } $e_key = $is_new ? true : null; $e_value = true; $errors = array(); $key = $var->getVariableKey(); $display_value = null; $value = $var->getVariableValue(); if ($request->isFormPost()) { if ($is_new) { $key = $request->getStr('key'); if (!strlen($key)) { $errors[] = pht('Variable key is required.'); $e_key = pht('Required'); } else { if (!preg_match('/^[a-z0-9.-]+\\z/', $key)) { $errors[] = pht('Variable key "%s" must contain only lowercase letters, digits, ' . 'period, and hyphen.', $key); $e_key = pht('Invalid'); } } } $raw_value = $request->getStr('value'); $value = json_decode($raw_value, true); if ($value === null && strtolower($raw_value) !== 'null') { $e_value = pht('Invalid'); $errors[] = pht('Variable value must be valid JSON.'); $display_value = $raw_value; } if (!$errors) { $editor = id(new PhluxVariableEditor())->setActor($viewer)->setContinueOnNoEffect(true)->setContentSourceFromRequest($request); $xactions = array(); $xactions[] = id(new PhluxTransaction())->setTransactionType(PhluxTransaction::TYPE_EDIT_KEY)->setNewValue($key); $xactions[] = id(new PhluxTransaction())->setTransactionType(PhluxTransaction::TYPE_EDIT_VALUE)->setNewValue($value); $xactions[] = id(new PhluxTransaction())->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)->setNewValue($request->getStr('viewPolicy')); $xactions[] = id(new PhluxTransaction())->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)->setNewValue($request->getStr('editPolicy')); try { $editor->applyTransactions($var, $xactions); $view_uri = $this->getApplicationURI('/view/' . $key . '/'); return id(new AphrontRedirectResponse())->setURI($view_uri); } catch (AphrontDuplicateKeyQueryException $ex) { $e_key = pht('Not Unique'); $errors[] = pht('Variable key must be unique.'); } } } if ($display_value === null) { if (is_array($value) && array_keys($value) !== array_keys(array_values($value))) { $json = new PhutilJSON(); $display_value = $json->encodeFormatted($value); } else { $display_value = json_encode($value); } } $policies = id(new PhabricatorPolicyQuery())->setViewer($viewer)->setObject($var)->execute(); $form = id(new AphrontFormView())->setUser($viewer)->appendChild(id(new AphrontFormTextControl())->setValue($var->getVariableKey())->setLabel(pht('Key'))->setName('key')->setError($e_key)->setCaption(pht('Lowercase letters, digits, dot and hyphen only.'))->setDisabled(!$is_new))->appendChild(id(new AphrontFormTextAreaControl())->setValue($display_value)->setLabel(pht('Value'))->setName('value')->setCaption(pht('Enter value as JSON.'))->setError($e_value))->appendChild(id(new AphrontFormPolicyControl())->setName('viewPolicy')->setPolicyObject($var)->setCapability(PhabricatorPolicyCapability::CAN_VIEW)->setPolicies($policies))->appendChild(id(new AphrontFormPolicyControl())->setName('editPolicy')->setPolicyObject($var)->setCapability(PhabricatorPolicyCapability::CAN_EDIT)->setPolicies($policies)); if ($is_new) { $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Create Variable'))); } else { $form->appendChild(id(new AphrontFormSubmitControl())->setValue(pht('Update Variable'))->addCancelButton($view_uri)); } $crumbs = $this->buildApplicationCrumbs(); if ($is_new) { $title = pht('Create Variable'); $crumbs->addTextCrumb($title, $request->getRequestURI()); $header_icon = 'fa-plus-square'; } else { $title = pht('Edit Variable: %s', $key); $header_icon = 'fa-pencil'; $crumbs->addTextCrumb($title, $request->getRequestURI()); } $crumbs->setBorder(true); $box = id(new PHUIObjectBoxView())->setHeaderText(pht('Variable'))->setFormErrors($errors)->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)->setForm($form); $header = id(new PHUIHeaderView())->setHeader($title)->setHeaderIcon($header_icon); $view = id(new PHUITwoColumnView())->setHeader($header)->setFooter(array($box)); return $this->newPage()->setTitle($title)->setCrumbs($crumbs)->appendChild($view); }
public function getMethodDescription() { $messages = HarbormasterMessageType::getAllMessages(); $head_type = pht('Constant'); $head_desc = pht('Description'); $head_key = pht('Key'); $head_type = pht('Type'); $head_name = pht('Name'); $rows = array(); $rows[] = "| {$head_type} | {$head_desc} |"; $rows[] = '|--------------|--------------|'; foreach ($messages as $message) { $description = HarbormasterMessageType::getMessageDescription($message); $rows[] = "| `{$message}` | {$description} |"; } $message_table = implode("\n", $rows); $rows = array(); $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; $rows[] = '|-------------|--------------|--------------|'; $unit_spec = HarbormasterBuildUnitMessage::getParameterSpec(); foreach ($unit_spec as $key => $parameter) { $type = idx($parameter, 'type'); $type = str_replace('|', ' ' . pht('or') . ' ', $type); $description = idx($parameter, 'description'); $rows[] = "| `{$key}` | //{$type}// | {$description} |"; } $unit_table = implode("\n", $rows); $rows = array(); $rows[] = "| {$head_key} | {$head_name} | {$head_desc} |"; $rows[] = '|-------------|--------------|--------------|'; $results = ArcanistUnitTestResult::getAllResultCodes(); foreach ($results as $result_code) { $name = ArcanistUnitTestResult::getResultCodeName($result_code); $description = ArcanistUnitTestResult::getResultCodeDescription($result_code); $rows[] = "| `{$result_code}` | **{$name}** | {$description} |"; } $result_table = implode("\n", $rows); $rows = array(); $rows[] = "| {$head_key} | {$head_type} | {$head_desc} |"; $rows[] = '|-------------|--------------|--------------|'; $lint_spec = HarbormasterBuildLintMessage::getParameterSpec(); foreach ($lint_spec as $key => $parameter) { $type = idx($parameter, 'type'); $type = str_replace('|', ' ' . pht('or') . ' ', $type); $description = idx($parameter, 'description'); $rows[] = "| `{$key}` | //{$type}// | {$description} |"; } $lint_table = implode("\n", $rows); $rows = array(); $rows[] = "| {$head_key} | {$head_name} |"; $rows[] = '|-------------|--------------|'; $severities = ArcanistLintSeverity::getLintSeverities(); foreach ($severities as $key => $name) { $rows[] = "| `{$key}` | **{$name}** |"; } $severity_table = implode("\n", $rows); $valid_unit = array(array('name' => 'PassingTest', 'result' => ArcanistUnitTestResult::RESULT_PASS), array('name' => 'FailingTest', 'result' => ArcanistUnitTestResult::RESULT_FAIL)); $valid_lint = array(array('name' => pht('Syntax Error'), 'code' => 'EXAMPLE1', 'severity' => ArcanistLintSeverity::SEVERITY_ERROR, 'path' => 'path/to/example.c', 'line' => 17, 'char' => 3), array('name' => pht('Not A Haiku'), 'code' => 'EXAMPLE2', 'severity' => ArcanistLintSeverity::SEVERITY_ERROR, 'path' => 'path/to/source.cpp', 'line' => 23, 'char' => 1, 'description' => pht('This function definition is not a haiku.'))); $json = new PhutilJSON(); $valid_unit = $json->encodeAsList($valid_unit); $valid_lint = $json->encodeAsList($valid_lint); return pht("Send a message about the status of a build target to Harbormaster, " . "notifying the application of build results in an external system." . "\n\n" . "Sending Messages\n" . "================\n" . "If you run external builds, you can use this method to publish build " . "results back into Harbormaster after the external system finishes work " . "or as it makes progress." . "\n\n" . "The simplest way to use this method is to call it once after the " . "build finishes with a `pass` or `fail` message. This will record the " . "build result, and continue the next step in the build if the build was " . "waiting for a result." . "\n\n" . "When you send a status message about a build target, you can " . "optionally include detailed `lint` or `unit` results alongside the " . "message. See below for details." . "\n\n" . "If you want to report intermediate results but a build hasn't " . "completed yet, you can use the `work` message. This message doesn't " . "have any direct effects, but allows you to send additional data to " . "update the progress of the build target. The target will continue " . "waiting for a completion message, but the UI will update to show the " . "progress which has been made." . "\n\n" . "Message Types\n" . "=============\n" . "When you send Harbormaster a message, you must include a `type`, " . "which describes the overall state of the build. For example, use " . "`pass` to tell Harbomaster that a build completed successfully." . "\n\n" . "Supported message types are:" . "\n\n" . "%s" . "\n\n" . "Unit Results\n" . "============\n" . "You can report test results alongside a message. The simplest way to " . "do this is to report all the results alongside a `pass` or `fail` " . "message, but you can also send a `work` message to report intermediate " . "results.\n\n" . "To provide unit test results, pass a list of results in the `unit` " . "parameter. Each result shoud be a dictionary with these keys:" . "\n\n" . "%s" . "\n\n" . "The `result` parameter recognizes these test results:" . "\n\n" . "%s" . "\n\n" . "This is a simple, valid value for the `unit` parameter. It reports " . "one passing test and one failing test:\n\n" . "\n\n" . "```lang=json\n" . "%s" . "```" . "\n\n" . "Lint Results\n" . "============\n" . "Like unit test results, you can report lint results alongside a " . "message. The `lint` parameter should contain results as a list of " . "dictionaries with these keys:" . "\n\n" . "%s" . "\n\n" . "The `severity` parameter recognizes these severity levels:" . "\n\n" . "%s" . "\n\n" . "This is a simple, valid value for the `lint` parameter. It reports one " . "error and one warning:" . "\n\n" . "```lang=json\n" . "%s" . "```" . "\n\n", $message_table, $unit_table, $result_table, $valid_unit, $lint_table, $severity_table, $valid_lint); }
// Report only the first use of a symbol, since reporting all of them // isn't terribly informative. continue; } if (!empty($declared_symbols[$type][$name])) { // We declare this symbol, so don't treat it as a requirement. continue; } $required_symbols[$type][$name] = $spec['symbol']->getOffset(); } $result = array('have' => $declared_symbols, 'need' => $required_symbols, 'xmap' => $xmap); // -( Output )---------------------------------------------------------------- if ($args->getArg('ugly')) { echo json_encode($result); } else { $json = new PhutilJSON(); echo $json->encodeFormatted($result); } // -( Library )--------------------------------------------------------------- function phutil_symbols_get_builtins() { $builtin = array(); $builtin['classes'] = get_declared_classes(); $builtin['interfaces'] = get_declared_interfaces(); $funcs = get_defined_functions(); $builtin['functions'] = $funcs['internal']; foreach (array('functions', 'classes') as $type) { // Developers may not have every extension that a library potentially uses // installed. We supplement the list of declared functions and classses with // a list of known extension functions to avoid raising false positives just // because you don't have pcntl, etc.
private function executeParserTest($name, $data) { $data = explode("\n", $data, 2); if (count($data) !== 2) { throw new Exception(pht('Expected multiple lines in parser test file "%s".', $name)); } $head = head($data); $body = last($data); if (!preg_match('/^#/', $head)) { throw new Exception(pht('Expected first line of parser test file "%s" to begin with "#" ' . 'and specify test options.', $name)); } $head = preg_replace('/^#\\s*/', '', $head); $options_parser = new PhutilSimpleOptions(); $options = $options_parser->parse($head); $type = null; foreach ($options as $key => $value) { switch ($key) { case 'pass': case 'fail-syntax': case 'fail-parse': if ($type !== null) { throw new Exception(pht('Test file "%s" unexpectedly specifies multiple expected ', 'test outcomes.', $name)); } $type = $key; break; case 'comment': // Human readable comment providing test case information. break; case 'rtrim': // Allows construction of tests which rely on EOF without newlines. $body = rtrim($body); break; default: throw new Exception(pht('Test file "%s" has unknown option "%s" in its options ' . 'string.', $name, $key)); } } if ($type === null) { throw new Exception(pht('Test file "%s" does not specify a test result (like "pass") in ' . 'its options string.', $name)); } $future = xhpast_get_parser_future($body); list($err, $stdout, $stderr) = $future->resolve(); switch ($type) { case 'pass': case 'fail-parse': $this->assertEqual(0, $err, pht('Exit code for "%s".', $name)); $expect_name = preg_replace('/\\.test$/', '.expect', $name); $dir = dirname(__FILE__) . '/data/'; $expect = Filesystem::readFile($dir . $expect_name); $expect = json_decode($expect, true); if (!is_array($expect)) { throw new Exception(pht('Test ".expect" file "%s" for test "%s" is not valid JSON.', $expect_name, $name)); } $stdout = json_decode($stdout, true); if (!is_array($stdout)) { throw new Exception(pht('Output for test file "%s" is not valid JSON.', $name)); } $json = new PhutilJSON(); $expect_nice = $json->encodeFormatted($expect); $stdout_nice = $json->encodeFormatted($stdout); if ($type == 'pass') { $this->assertEqual($expect_nice, $stdout_nice, pht('Parser output for "%s".', $name)); } else { $this->assertFalse($expect_nice == $stdout_nice, pht('Expected parser to parse "%s" incorrectly.', $name)); } break; case 'fail-syntax': $this->assertEqual(1, $err, pht('Exit code for "%s".', $name)); $this->assertTrue((bool) preg_match('/syntax error/', $stderr), pht('Expect "syntax error" in stderr or "%s".', $name)); break; } }
public function getOptions() { $priority_type = 'custom:ManiphestPriorityConfigOptionType'; $priority_defaults = array(100 => array('name' => pht('Unbreak Now!'), 'short' => pht('Unbreak!'), 'color' => 'pink', 'keywords' => array('unbreak')), 90 => array('name' => pht('Needs Triage'), 'short' => pht('Triage'), 'color' => 'violet', 'keywords' => array('triage')), 80 => array('name' => pht('High'), 'short' => pht('High'), 'color' => 'red', 'keywords' => array('high')), 50 => array('name' => pht('Normal'), 'short' => pht('Normal'), 'color' => 'orange', 'keywords' => array('normal')), 25 => array('name' => pht('Low'), 'short' => pht('Low'), 'color' => 'yellow', 'keywords' => array('low')), 0 => array('name' => pht('Wishlist'), 'short' => pht('Wish'), 'color' => 'sky', 'keywords' => array('wish', 'wishlist'))); $status_type = 'custom:ManiphestStatusConfigOptionType'; $status_defaults = array('open' => array('name' => pht('Open'), 'special' => ManiphestTaskStatus::SPECIAL_DEFAULT, 'prefixes' => array('open', 'opens', 'reopen', 'reopens')), 'resolved' => array('name' => pht('Resolved'), 'name.full' => pht('Closed, Resolved'), 'closed' => true, 'special' => ManiphestTaskStatus::SPECIAL_CLOSED, 'transaction.icon' => 'fa-check-circle', 'prefixes' => array('closed', 'closes', 'close', 'fix', 'fixes', 'fixed', 'resolve', 'resolves', 'resolved'), 'suffixes' => array('as resolved', 'as fixed'), 'keywords' => array('closed', 'fixed', 'resolved')), 'wontfix' => array('name' => pht('Wontfix'), 'name.full' => pht('Closed, Wontfix'), 'transaction.icon' => 'fa-ban', 'closed' => true, 'prefixes' => array('wontfix', 'wontfixes', 'wontfixed'), 'suffixes' => array('as wontfix')), 'invalid' => array('name' => pht('Invalid'), 'name.full' => pht('Closed, Invalid'), 'transaction.icon' => 'fa-minus-circle', 'closed' => true, 'claim' => false, 'prefixes' => array('invalidate', 'invalidates', 'invalidated'), 'suffixes' => array('as invalid')), 'duplicate' => array('name' => pht('Duplicate'), 'name.full' => pht('Closed, Duplicate'), 'transaction.icon' => 'fa-files-o', 'special' => ManiphestTaskStatus::SPECIAL_DUPLICATE, 'closed' => true, 'claim' => false), 'spite' => array('name' => pht('Spite'), 'name.full' => pht('Closed, Spite'), 'name.action' => pht('Spited'), 'transaction.icon' => 'fa-thumbs-o-down', 'silly' => true, 'closed' => true, 'prefixes' => array('spite', 'spites', 'spited'), 'suffixes' => array('out of spite', 'as spite'))); $status_description = $this->deformat(pht(<<<EOTEXT Allows you to edit, add, or remove the task statuses available in Maniphest, like "Open", "Resolved" and "Invalid". The configuration should contain a map of status constants to status specifications (see defaults below for examples). The constant for each status should be 1-12 characters long and contain only lowercase letters and digits. Valid examples are "open", "closed", and "invalid". Users will not normally see these values. The keys you can provide in a specification are: - `name` //Required string.// Name of the status, like "Invalid". - `name.full` //Optional string.// Longer name, like "Closed, Invalid". This appears on the task detail view in the header. - `name.action` //Optional string.// Action name for email subjects, like "Marked Invalid". - `closed` //Optional bool.// Statuses are either "open" or "closed". Specifying `true` here will mark the status as closed (like "Resolved" or "Invalid"). By default, statuses are open. - `special` //Optional string.// Mark this status as special. The special statuses are: - `default` This is the default status for newly created tasks. You must designate one status as default, and it must be an open status. - `closed` This is the default status for closed tasks (for example, tasks closed via the "!close" action in email or via the quick close button in Maniphest). You must designate one status as the default closed status, and it must be a closed status. - `duplicate` This is the status used when tasks are merged into one another as duplicates. You must designate one status for duplicates, and it must be a closed status. - `transaction.icon` //Optional string.// Allows you to choose a different icon to use for this status when showing status changes in the transaction log. Please see UIExamples, Icons and Images for a list. - `transaction.color` //Optional string.// Allows you to choose a different color to use for this status when showing status changes in the transaction log. - `silly` //Optional bool.// Marks this status as silly, and thus wholly inappropriate for use by serious businesses. - `prefixes` //Optional list<string>.// Allows you to specify a list of text prefixes which will trigger a task transition into this status when mentioned in a commit message. For example, providing "closes" here will allow users to move tasks to this status by writing `Closes T123` in commit messages. - `suffixes` //Optional list<string>.// Allows you to specify a list of text suffixes which will trigger a task transition into this status when mentioned in a commit message, after a valid prefix. For example, providing "as invalid" here will allow users to move tasks to this status by writing `Closes T123 as invalid`, even if another status is selected by the "Closes" prefix. - `keywords` //Optional list<string>.// Allows you to specify a list of keywords which can be used with `!status` commands in email to select this status. - `disabled` //Optional bool.// Marks this status as no longer in use so tasks can not be created or edited to have this status. Existing tasks with this status will not be affected, but you can batch edit them or let them die out on their own. - `claim` //Optional bool.// By default, closing an unassigned task claims it. You can set this to `false` to disable this behavior for a particular status. Statuses will appear in the UI in the order specified. Note the status marked `special` as `duplicate` is not settable directly and will not appear in UI elements, and that any status marked `silly` does not appear if Phabricator is configured with `phabricator.serious-business` set to true. Examining the default configuration and examples below will probably be helpful in understanding these options. EOTEXT )); $status_example = array('open' => array('name' => pht('Open'), 'special' => 'default'), 'closed' => array('name' => pht('Closed'), 'special' => 'closed', 'closed' => true), 'duplicate' => array('name' => pht('Duplicate'), 'special' => 'duplicate', 'closed' => true)); $json = new PhutilJSON(); $status_example = $json->encodeFormatted($status_example); // This is intentionally blank for now, until we can move more Maniphest // logic to custom fields. $default_fields = array(); foreach ($default_fields as $key => $enabled) { $default_fields[$key] = array('disabled' => !$enabled); } $custom_field_type = 'custom:PhabricatorCustomFieldConfigOptionType'; $fields_example = array('mycompany.estimated-hours' => array('name' => pht('Estimated Hours'), 'type' => 'int', 'caption' => pht('Estimated number of hours this will take.'))); $fields_json = id(new PhutilJSON())->encodeFormatted($fields_example); $points_type = 'custom:ManiphestPointsConfigOptionType'; $points_example_1 = array('enabled' => true, 'label' => pht('Story Points'), 'action' => pht('Change Story Points')); $points_json_1 = id(new PhutilJSON())->encodeFormatted($points_example_1); $points_example_2 = array('enabled' => true, 'label' => pht('Estimated Hours'), 'action' => pht('Change Estimate')); $points_json_2 = id(new PhutilJSON())->encodeFormatted($points_example_2); $points_description = $this->deformat(pht(<<<EOTEXT Activates a points field on tasks. You can use points for estimation or planning. If configured, points will appear on workboards. To activate points, set this value to a map with these keys: - `enabled` //Optional bool.// Use `true` to enable points, or `false` to disable them. - `label` //Optional string.// Label for points, like "Story Points" or "Estimated Hours". If omitted, points will be called "Points". - `action` //Optional string.// Label for the action which changes points in Maniphest, like "Change Estimate". If omitted, the action will be called "Change Points". See the example below for a starting point. EOTEXT )); return array($this->newOption('maniphest.custom-field-definitions', 'wild', array())->setSummary(pht('Custom Maniphest fields.'))->setDescription(pht('Array of custom fields for Maniphest tasks. For details on ' . 'adding custom fields to Maniphest, see "Configuring Custom ' . 'Fields" in the documentation.'))->addExample($fields_json, pht('Valid setting')), $this->newOption('maniphest.fields', $custom_field_type, $default_fields)->setCustomData(id(new ManiphestTask())->getCustomFieldBaseClass())->setDescription(pht('Select and reorder task fields.')), $this->newOption('maniphest.priorities', $priority_type, $priority_defaults)->setSummary(pht('Configure Maniphest priority names.'))->setDescription(pht('Allows you to edit or override the default priorities available ' . 'in Maniphest, like "High", "Normal" and "Low". The configuration ' . 'should contain a map of priority constants to priority ' . 'specifications (see defaults below for examples).' . "\n\n" . 'The keys you can define for a priority are:' . "\n\n" . ' - `name` Name of the priority.' . "\n" . ' - `short` Alternate shorter name, used in UIs where there is ' . ' not much space available.' . "\n" . ' - `color` A color for this priority, like "red" or "blue".' . ' - `keywords` An optional list of keywords which can ' . ' be used to select this priority when using `!priority` ' . ' commands in email.' . "\n" . ' - `disabled` Optional boolean to prevent users from choosing ' . ' this priority when creating or editing tasks. Existing ' . ' tasks will be unaffected, and can be batch edited to a ' . ' different priority or left to eventually die out.' . "\n\n" . 'You can choose which priority is the default for newly created ' . 'tasks with `%s`.', 'maniphest.default-priority')), $this->newOption('maniphest.statuses', $status_type, $status_defaults)->setSummary(pht('Configure Maniphest task statuses.'))->setDescription($status_description)->addExample($status_example, pht('Minimal Valid Config')), $this->newOption('maniphest.default-priority', 'int', 90)->setSummary(pht('Default task priority for create flows.'))->setDescription(pht('Choose a default priority for newly created tasks. You can ' . 'review and adjust available priorities by using the ' . '%s configuration option. The default value (`90`) ' . 'corresponds to the default "Needs Triage" priority.', 'maniphest.priorities')), $this->newOption('metamta.maniphest.subject-prefix', 'string', '[Maniphest]')->setDescription(pht('Subject prefix for Maniphest mail.')), $this->newOption('maniphest.points', $points_type, array())->setSummary(pht('Configure point values for tasks.'))->setDescription($points_description)->addExample($points_json_1, pht('Points Config'))->addExample($points_json_2, pht('Hours Config'))); }
public function execute(PhutilArgumentParser $args) { $this->readBookConfiguration($args->getArg('book')); $console = PhutilConsole::getConsole(); $atomizer_class = $args->getArg('atomizer'); if (!$atomizer_class) { throw new Exception('Specify an atomizer class with --atomizer.'); } $symbols = id(new PhutilSymbolLoader())->setName($atomizer_class)->setConcreteOnly(true)->setAncestorClass('DivinerAtomizer')->selectAndLoadSymbols(); if (!$symbols) { throw new Exception("Atomizer class '{$atomizer_class}' must be a concrete subclass of " . "DivinerAtomizer."); } $atomizer = newv($atomizer_class, array()); $files = $args->getArg('files'); if (!$files) { throw new Exception('Specify one or more files to atomize.'); } $file_atomizer = new DivinerFileAtomizer(); foreach (array($atomizer, $file_atomizer) as $configure) { $configure->setBook($this->getConfig('name')); } $group_rules = array(); foreach ($this->getConfig('groups', array()) as $group => $spec) { $include = (array) idx($spec, 'include', array()); foreach ($include as $pattern) { $group_rules[$pattern] = $group; } } $all_atoms = array(); $context = array('group' => null); foreach ($files as $file) { $abs_path = Filesystem::resolvePath($file, $this->getConfig('root')); $data = Filesystem::readFile($abs_path); if (!$this->shouldAtomizeFile($file, $data)) { $console->writeLog("Skipping %s...\n", $file); continue; } else { $console->writeLog("Atomizing %s...\n", $file); } $context['group'] = null; foreach ($group_rules as $rule => $group) { if (preg_match($rule, $file)) { $context['group'] = $group; break; } } $file_atoms = $file_atomizer->atomize($file, $data, $context); $all_atoms[] = $file_atoms; if (count($file_atoms) !== 1) { throw new Exception('Expected exactly one atom from file atomizer.'); } $file_atom = head($file_atoms); $atoms = $atomizer->atomize($file, $data, $context); foreach ($atoms as $atom) { if (!$atom->hasParent()) { $file_atom->addChild($atom); } } $all_atoms[] = $atoms; } $all_atoms = array_mergev($all_atoms); $all_atoms = mpull($all_atoms, 'toDictionary'); $all_atoms = ipull($all_atoms, null, 'hash'); if ($args->getArg('ugly')) { $json = json_encode($all_atoms); } else { $json_encoder = new PhutilJSON(); $json = $json_encoder->encodeFormatted($all_atoms); } $console->writeOut('%s', $json); return 0; }
public function writeLocalArcConfig(array $config) { $json_encoder = new PhutilJSON(); $json = $json_encoder->encodeFormatted($config); $dir = $this->localMetaDir; if (!strlen($dir)) { throw new Exception(pht('No working copy to write config into!')); } $local_dir = $dir . DIRECTORY_SEPARATOR . 'arc'; if (!Filesystem::pathExists($local_dir)) { Filesystem::createDirectory($local_dir, 0755); } $config_file = $local_dir . DIRECTORY_SEPARATOR . 'config'; Filesystem::writeFile($config_file, $json); }
function phutil_fail_on_unsupported_feature(XHPASTNode $node, $file, $what) { $line = $node->getLineNumber(); $message = phutil_console_wrap(pht('`%s` has limited support for features introduced after PHP 5.2.3. ' . 'This library uses an unsupported feature (%s) on line %d of %s.', 'arc liberate', $what, $line, Filesystem::readablePath($file))); $result = array('error' => $message, 'line' => $line, 'file' => $file); $json = new PhutilJSON(); echo $json->encodeFormatted($result); exit(0); }
private function renderAPIValue($value) { $json = new PhutilJSON(); if (is_array($value)) { $value = $json->encodeFormatted($value); } $value = phutil_tag('pre', array('style' => 'white-space: pre-wrap;'), $value); return $value; }
$args = new PhutilArgumentParser($argv); $args->setTagline('test client for Aphlict server'); $args->setSynopsis(<<<EOHELP **aphlict_test_client.php** [__options__] Connect to the Aphlict server configured in the Phabricator config. EOHELP ); $args->parseStandardArguments(); $args->parse(array(array('name' => 'server', 'param' => 'uri', 'default' => PhabricatorEnv::getEnvConfig('notification.client-uri'), 'help' => 'Connect to __uri__ instead of the default server.'))); $console = PhutilConsole::getConsole(); $errno = null; $errstr = null; $uri = $args->getArg('server'); $uri = new PhutilURI($uri); $uri->setProtocol('tcp'); $console->writeErr("Connecting...\n"); $socket = stream_socket_client($uri, $errno, $errstr); if (!$socket) { $console->writeErr("Unable to connect to Aphlict (at '{$uri}'). Error #{$errno}: {$errstr}"); exit(1); } else { $console->writeErr("Connected.\n"); } $io_channel = new PhutilSocketChannel($socket); $proto_channel = new PhutilJSONProtocolChannel($io_channel); $json = new PhutilJSON(); while (true) { $message = $proto_channel->waitForMessage(); $console->writeOut($json->encodeFormatted($message)); }
private function renderAPIValue($value) { $json = new PhutilJSON(); if (is_array($value)) { $value = $json->encodeFormatted($value); $value = phutil_escape_html($value); } else { $value = phutil_escape_html($value); } $value = '<pre style="white-space: pre-wrap;">' . $value . '</pre>'; return $value; }
public function renderChangeDetails(PhabricatorUser $viewer) { $json = new PhutilJSON(); switch ($this->getTransactionType()) { case self::TYPE_EDIT: return $this->renderTextCorpusChangeDetails($viewer, $json->encodeFormatted($this->getOldValue()), $json->encodeFormatted($this->getNewValue())); } return $this->renderTextCorpusChangeDetails($viewer, $this->getOldValue(), $this->getNewValue()); }