public function processRequest()
 {
     $request = $this->getRequest();
     $user = $request->getUser();
     $phid = head($request->getArr('macro'));
     $above = $request->getStr('above');
     $below = $request->getStr('below');
     $e_macro = true;
     $errors = array();
     if ($request->isDialogFormPost()) {
         if (!$phid) {
             $e_macro = pht('Required');
             $errors[] = pht('Macro name is required.');
         } else {
             $macro = id(new PhabricatorMacroQuery())->setViewer($user)->withPHIDs(array($phid))->executeOne();
             if (!$macro) {
                 $e_macro = pht('Invalid');
                 $errors[] = pht('No such macro.');
             }
         }
         if (!$errors) {
             $options = new PhutilSimpleOptions();
             $data = array('src' => $macro->getName(), 'above' => $above, 'below' => $below);
             $string = $options->unparse($data, $escape = '}');
             $result = array('text' => "{meme, {$string}}");
             return id(new AphrontAjaxResponse())->setContent($result);
         }
     }
     $view = id(new AphrontFormView())->setUser($user)->appendControl(id(new AphrontFormTokenizerControl())->setLabel(pht('Macro'))->setName('macro')->setLimit(1)->setDatasource(new PhabricatorMacroDatasource())->setError($e_macro))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Above'))->setName('above')->setValue($above))->appendChild(id(new AphrontFormTextControl())->setLabel(pht('Below'))->setName('below')->setValue($below));
     $dialog = id(new AphrontDialogView())->setUser($user)->setTitle(pht('Create Meme'))->appendForm($view)->addCancelButton('/')->addSubmitButton(pht('Llama Diorama'));
     return id(new AphrontDialogResponse())->setDialog($dialog);
 }
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $resource_type = $args->getArg('type');
     if (!$resource_type) {
         throw new PhutilArgumentUsageException(pht('Specify a resource type with `%s`.', '--type'));
     }
     $until = $args->getArg('until');
     if (strlen($until)) {
         $until = strtotime($until);
         if ($until <= 0) {
             throw new PhutilArgumentUsageException(pht('Unable to parse argument to "%s".', '--until'));
         }
     }
     $attributes = $args->getArg('attributes');
     if ($attributes) {
         $options = new PhutilSimpleOptions();
         $options->setCaseSensitive(true);
         $attributes = $options->parse($attributes);
     }
     $lease = id(new DrydockLease())->setResourceType($resource_type);
     if ($attributes) {
         $lease->setAttributes($attributes);
     }
     if ($until) {
         $lease->setUntil($until);
     }
     $lease->queueForActivation();
     echo tsprintf("%s\n", pht('Waiting for daemons to activate lease...'));
     $lease->waitUntilActive();
     echo tsprintf("%s\n", pht('Activated lease "%s".', $lease->getID()));
     return 0;
 }
 protected function renderObjectEmbed($object, $handle, $options)
 {
     $embed_paste = id(new PasteEmbedView())->setPaste($object)->setHandle($handle);
     if (strlen($options)) {
         $parser = new PhutilSimpleOptions();
         $opts = $parser->parse(substr($options, 1));
         foreach ($opts as $key => $value) {
             if ($key == 'lines') {
                 $embed_paste->setLines(preg_replace('/[^0-9]/', '', $value));
             } else {
                 if ($key == 'highlight') {
                     $highlights = preg_split('/,|&/', preg_replace('/\\s+/', '', $value));
                     $to_highlight = array();
                     foreach ($highlights as $highlight) {
                         $highlight = explode('-', $highlight);
                         if (!empty($highlight)) {
                             sort($highlight);
                             $to_highlight = array_merge($to_highlight, range(head($highlight), last($highlight)));
                         }
                     }
                     $embed_paste->setHighlights(array_unique($to_highlight));
                 }
             }
         }
     }
     return $embed_paste;
 }
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $resource_name = $args->getArg('name');
     if (!$resource_name) {
         throw new PhutilArgumentUsageException('Specify a resource name with `--name`.');
     }
     $blueprint_id = $args->getArg('blueprint');
     if (!$blueprint_id) {
         throw new PhutilArgumentUsageException('Specify a blueprint ID with `--blueprint`.');
     }
     $attributes = $args->getArg('attributes');
     if ($attributes) {
         $options = new PhutilSimpleOptions();
         $options->setCaseSensitive(true);
         $attributes = $options->parse($attributes);
     }
     $viewer = $this->getViewer();
     $blueprint = id(new DrydockBlueprintQuery())->setViewer($viewer)->withIDs(array($blueprint_id))->executeOne();
     if (!$blueprint) {
         throw new PhutilArgumentUsageException('Specified blueprint does not exist.');
     }
     $resource = id(new DrydockResource())->setBlueprintPHID($blueprint->getPHID())->setType($blueprint->getImplementation()->getType())->setName($resource_name)->setStatus(DrydockResourceStatus::STATUS_OPEN);
     if ($attributes) {
         $resource->setAttributes($attributes);
     }
     $resource->save();
     $console->writeOut("Created Resource %s\n", $resource->getID());
     return 0;
 }
 public function markupNavigation(array $matches)
 {
     if (!$this->isFlatText($matches[0])) {
         return $matches[0];
     }
     $elements = ltrim($matches[1], ", \n");
     $elements = explode('>', $elements);
     $defaults = array('name' => null, 'type' => 'link', 'href' => null, 'icon' => null);
     $sequence = array();
     $parser = new PhutilSimpleOptions();
     foreach ($elements as $element) {
         if (strpos($element, '=') === false) {
             $sequence[] = array('name' => trim($element)) + $defaults;
         } else {
             $sequence[] = $parser->parse($element) + $defaults;
         }
     }
     if ($this->getEngine()->isTextMode()) {
         return implode(' > ', ipull($sequence, 'name'));
     }
     static $icon_names;
     if (!$icon_names) {
         $icon_names = array_fuse(PHUIIconView::getIcons());
     }
     $out = array();
     foreach ($sequence as $item) {
         $item_name = $item['name'];
         $item_color = PHUITagView::COLOR_GREY;
         if ($item['type'] == 'instructions') {
             $item_name = phutil_tag('em', array(), $item_name);
             $item_color = PHUITagView::COLOR_INDIGO;
         }
         $tag = id(new PHUITagView())->setType(PHUITagView::TYPE_SHADE)->setShade($item_color)->setName($item_name);
         if ($item['icon']) {
             $icon_name = 'fa-' . $item['icon'];
             if (isset($icon_names[$icon_name])) {
                 $tag->setIcon($icon_name);
             }
         }
         if ($item['href'] !== null) {
             if (PhabricatorEnv::isValidRemoteURIForLink($item['href'])) {
                 $tag->setHref($item['href']);
                 $tag->setExternal(true);
             }
         }
         $out[] = $tag;
     }
     if ($this->getEngine()->isHTMLMailMode()) {
         $arrow_attr = array('style' => 'color: #92969D;');
         $nav_attr = array();
     } else {
         $arrow_attr = array('class' => 'remarkup-nav-sequence-arrow');
         $nav_attr = array('class' => 'remarkup-nav-sequence');
     }
     $joiner = phutil_tag('span', $arrow_attr, " → ");
     $out = phutil_implode_html($joiner, $out);
     $out = phutil_tag('span', $nav_attr, $out);
     return $this->getEngine()->storeText($out);
 }
 private function getFileOptions($option_string)
 {
     $options = array('size' => null, 'layout' => 'left', 'float' => false, 'width' => null, 'height' => null, 'alt' => null);
     if ($option_string) {
         $option_string = trim($option_string, ', ');
         $parser = new PhutilSimpleOptions();
         $options = $parser->parse($option_string) + $options;
     }
     return $options;
 }
 public function markupIcon(array $matches)
 {
     $engine = $this->getEngine();
     $text_mode = $engine->isTextMode();
     $mail_mode = $engine->isHTMLMailMode();
     if (!$this->isFlatText($matches[0]) || $text_mode || $mail_mode) {
         return $matches[0];
     }
     $extra = idx($matches, 1);
     // We allow various forms, like these:
     //
     //   {icon}
     //   {icon camera}
     //   {icon,camera}
     //   {icon camera color=red}
     //   {icon, camera, color=red}
     $extra = ltrim($extra, ", \n");
     $extra = preg_split('/[\\s,]+/', $extra, 2);
     // Choose some arbitrary default icon so that previews render in a mostly
     // reasonable way as you're typing the syntax.
     $icon = idx($extra, 0, 'paw');
     $defaults = array('color' => null, 'spin' => false);
     $options = idx($extra, 1, '');
     $parser = new PhutilSimpleOptions();
     $options = $parser->parse($options) + $defaults;
     // NOTE: We're validating icon and color names to prevent users from
     // adding arbitrary CSS classes to the document. Although this probably
     // isn't dangerous, it's safer to validate.
     static $icon_names;
     if (!$icon_names) {
         $icon_names = array_fuse(PHUIIconView::getFontIcons());
     }
     static $color_names;
     if (!$color_names) {
         $color_names = array_fuse(PHUIIconView::getFontIconColors());
     }
     if (empty($icon_names['fa-' . $icon])) {
         $icon = 'paw';
     }
     $color = $options['color'];
     if (empty($color_names[$color])) {
         $color = null;
     }
     $classes = array();
     $classes[] = $color;
     $spin = $options['spin'];
     if ($spin) {
         $classes[] = 'ph-spin';
     }
     $icon_view = id(new PHUIIconView())->setIconFont('fa-' . $icon, implode(' ', $classes));
     return $this->getEngine()->storeText($icon_view);
 }
예제 #8
0
 protected function renderObjectEmbed($object, $handle, $options)
 {
     $embed_mock = id(new PholioMockEmbedView())->setMock($object);
     if (strlen($options)) {
         $parser = new PhutilSimpleOptions();
         $opts = $parser->parse(substr($options, 1));
         if (isset($opts['image'])) {
             $images = array_unique(explode('&', preg_replace('/\\s+/', '', $opts['image'])));
             $embed_mock->setImages($images);
         }
     }
     return $embed_mock->render();
 }
 protected final function applyRules($input, &$count, array $rules)
 {
     if (++$count > $this->limit) {
         throw new Exception("Token replacement count exceeded limit!");
     }
     $matches = null;
     preg_match_all('/(\\[[^\\]]+\\])/', $input, $matches, PREG_OFFSET_CAPTURE);
     foreach (array_reverse($matches[1]) as $token_spec) {
         list($token, $offset) = $token_spec;
         $token_name = substr($token, 1, -1);
         $options = array();
         if ($name_end = strpos($token_name, ',')) {
             $options_parser = new PhutilSimpleOptions();
             $options = $options_parser->parse($token_name);
             $token_name = substr($token_name, 0, $name_end);
         }
         if (empty($rules[$token_name])) {
             throw new Exception("Invalid token '{$token_name}' in grammar.");
         }
         $key = array_rand($rules[$token_name]);
         $replacement = $this->applyRules($rules[$token_name][$key], $count, $rules);
         if (isset($options['indent'])) {
             if (is_numeric($options['indent'])) {
                 $replacement = self::strPadLines($replacement, $options['indent']);
             } else {
                 $replacement = self::strPadLines($replacement);
             }
         }
         if (isset($options['trim'])) {
             switch ($options['trim']) {
                 case 'left':
                     $replacement = ltrim($replacement);
                     break;
                 case 'right':
                     $replacement = rtrim($replacement);
                     break;
                 default:
                 case 'both':
                     $replacement = trim($replacement);
                     break;
             }
         }
         if (isset($options['block'])) {
             $replacement = "\n" . $replacement . "\n";
         }
         $input = substr_replace($input, $replacement, $offset, strlen($token));
     }
     return $input;
 }
 public function markupMeme($matches)
 {
     if (!$this->isFlatText($matches[0])) {
         return $matches[0];
     }
     $options = array('src' => null, 'above' => null, 'below' => null);
     $parser = new PhutilSimpleOptions();
     $options = $parser->parse($matches[1]) + $options;
     $uri = id(new PhutilURI('/macro/meme/'))->alter('macro', $options['src'])->alter('uppertext', $options['above'])->alter('lowertext', $options['below']);
     if ($this->getEngine()->isTextMode()) {
         $img = ($options['above'] != '' ? "\"{$options['above']}\"\n" : '') . $options['src'] . ' <' . PhabricatorEnv::getProductionURI($uri) . '>' . ($options['below'] != '' ? "\n\"{$options['below']}\"" : '');
     } else {
         $alt_text = pht('Macro %s: %s %s', $options['src'], $options['above'], $options['below']);
         $img = $this->newTag('img', array('src' => $uri, 'alt' => $alt_text));
     }
     return $this->getEngine()->storeText($img);
 }
 public function testTransformation()
 {
     $files = dirname(__FILE__) . '/transformer/';
     foreach (Filesystem::listDirectory($files) as $file) {
         $name = basename($file);
         $data = Filesystem::readFile($files . '/' . $file);
         $parts = preg_split('/^~~~+\\n/m', $data);
         $parts = array_merge($parts, array(null));
         list($options, $in, $expect) = $parts;
         $parser = new PhutilSimpleOptions();
         $options = $parser->parse($options) + array('minify' => false, 'name' => $name);
         $xformer = new CelerityResourceTransformer();
         $xformer->setRawURIMap(array('/rsrc/example.png' => '/res/hash/example.png'));
         $xformer->setMinify($options['minify']);
         $result = $xformer->transformResource($options['name'], $in);
         $this->assertEqual(rtrim($expect), rtrim($result), $file);
     }
 }
 public function execute(PhutilArgumentParser $args)
 {
     $viewer = $this->getViewer();
     $resource_type = $args->getArg('type');
     if (!$resource_type) {
         throw new PhutilArgumentUsageException(pht('Specify a resource type with `%s`.', '--type'));
     }
     $until = $args->getArg('until');
     if (strlen($until)) {
         $until = strtotime($until);
         if ($until <= 0) {
             throw new PhutilArgumentUsageException(pht('Unable to parse argument to "%s".', '--until'));
         }
     }
     $attributes = $args->getArg('attributes');
     if ($attributes) {
         $options = new PhutilSimpleOptions();
         $options->setCaseSensitive(true);
         $attributes = $options->parse($attributes);
     }
     $lease = id(new DrydockLease())->setResourceType($resource_type);
     $drydock_phid = id(new PhabricatorDrydockApplication())->getPHID();
     $lease->setAuthorizingPHID($drydock_phid);
     // TODO: This is not hugely scalable, although this is a debugging workflow
     // so maybe it's fine. Do we even need `bin/drydock lease` in the long run?
     $all_blueprints = id(new DrydockBlueprintQuery())->setViewer($viewer)->execute();
     $allowed_phids = mpull($all_blueprints, 'getPHID');
     if (!$allowed_phids) {
         throw new Exception(pht('No blueprints exist which can plausibly allocate resources to ' . 'satisfy the requested lease.'));
     }
     $lease->setAllowedBlueprintPHIDs($allowed_phids);
     if ($attributes) {
         $lease->setAttributes($attributes);
     }
     if ($until) {
         $lease->setUntil($until);
     }
     $lease->queueForActivation();
     echo tsprintf("%s\n", pht('Waiting for daemons to activate lease...'));
     $lease->waitUntilActive();
     echo tsprintf("%s\n", pht('Activated lease "%s".', $lease->getID()));
     return 0;
 }
 public function testSimpleOptionsUnparse()
 {
     $map = array('' => array(), 'legs=4' => array('legs' => '4'), 'legs=4, eyes=2' => array('legs' => '4', 'eyes' => '2'), 'eyes=2, legs=4' => array('eyes' => '2', 'legs' => '4'), 'legs=4, head' => array('legs' => '4', 'head' => true), 'eyes=2' => array('legs' => '', 'eyes' => '2'), '"thousands separator"=","' => array('thousands separator' => ','));
     foreach ($map as $expect => $dict) {
         $parser = new PhutilSimpleOptions();
         $this->assertEqual($expect, $parser->unparse($dict), "Correct unparse of " . print_r($dict, true));
     }
     $bogus = array(array('' => ''), array('' => 'x'));
     foreach ($bogus as $bad_input) {
         $caught = null;
         try {
             $parser = new PhutilSimpleOptions();
             $parser->unparse($bad_input);
         } catch (Exception $ex) {
             $caught = $ex;
         }
         $this->assertTrue($caught instanceof Exception, "Correct throw on unparse of bad input.");
     }
     $parser = new PhutilSimpleOptions();
     $this->assertEqual('a="\\}"', $parser->unparse(array('a' => '}'), '}'), "Unparse with extra escape.");
 }
 public function execute(PhutilArgumentParser $args)
 {
     $console = PhutilConsole::getConsole();
     $resource_type = $args->getArg('type');
     if (!$resource_type) {
         throw new PhutilArgumentUsageException(pht('Specify a resource type with `%s`.', '--type'));
     }
     $attributes = $args->getArg('attributes');
     if ($attributes) {
         $options = new PhutilSimpleOptions();
         $options->setCaseSensitive(true);
         $attributes = $options->parse($attributes);
     }
     $lease = id(new DrydockLease())->setResourceType($resource_type);
     if ($attributes) {
         $lease->setAttributes($attributes);
     }
     $lease->queueForActivation()->waitUntilActive();
     $console->writeOut("%s\n", pht('Acquired Lease %s', $lease->getID()));
     return 0;
 }
 public function markupImage(array $matches)
 {
     if (!$this->isFlatText($matches[0])) {
         return $matches[0];
     }
     $args = array();
     $defaults = array('uri' => null, 'alt' => null, 'href' => null, 'width' => null, 'height' => null);
     $trimmed_match = trim($matches[2]);
     if ($this->isURI($trimmed_match)) {
         $args['uri'] = new PhutilURI($trimmed_match);
     } else {
         $parser = new PhutilSimpleOptions();
         $keys = $parser->parse($trimmed_match);
         $uri_key = '';
         foreach (array('src', 'uri', 'url') as $key) {
             if (array_key_exists($key, $keys)) {
                 $uri_key = $key;
             }
         }
         if ($uri_key) {
             $args['uri'] = new PhutilURI($keys[$uri_key]);
         }
         $args += $keys;
     }
     $args += $defaults;
     if ($args['href'] && !PhabricatorEnv::isValidURIForLink($args['href'])) {
         $args['href'] = null;
     }
     if ($args['uri']) {
         $src_uri = id(new PhutilURI('/file/imageproxy/'))->setQueryParam('uri', (string) $args['uri']);
         $img = $this->newTag('img', array('src' => $src_uri, 'alt' => $args['alt'], 'href' => $args['href'], 'width' => $args['width'], 'height' => $args['height']));
         return $this->getEngine()->storeText($img);
     } else {
         return $matches[0];
     }
 }
 public function testSimpleOptionsUnparse()
 {
     $map = array('' => array(), 'legs=4' => array('legs' => '4'), 'legs=4, eyes=2' => array('legs' => '4', 'eyes' => '2'), 'eyes=2, legs=4' => array('eyes' => '2', 'legs' => '4'), 'legs=4, head' => array('legs' => '4', 'head' => true), 'eyes=2' => array('legs' => '', 'eyes' => '2'));
     foreach ($map as $expect => $dict) {
         $this->assertEqual($expect, PhutilSimpleOptions::unparse($dict), "Correct unparse of " . print_r($dict, true));
     }
     $bogus = array(array('LEGS' => true), array('LEGS' => 4), array('!' => '!'), array('' => '2'));
     foreach ($bogus as $bad_input) {
         $caught = null;
         try {
             PhutilSimpleOptions::unparse($bad_input);
         } catch (Exception $ex) {
             $caught = $ex;
         }
         $this->assertEqual(true, $caught instanceof Exception, "Correct throw on unparse of bad input.");
     }
 }
 public function markupText($text, $children)
 {
     if (preg_match('/^\\s*```/', $text)) {
         // If this is a ```-style block, trim off the backticks and any leading
         // blank line.
         $text = preg_replace('/^\\s*```(\\s*\\n)?/', '', $text);
         $text = preg_replace('/```\\s*$/', '', $text);
     }
     $lines = explode("\n", $text);
     while ($lines && !strlen(last($lines))) {
         unset($lines[last_key($lines)]);
     }
     $options = array('counterexample' => false, 'lang' => null, 'name' => null, 'lines' => null);
     $parser = new PhutilSimpleOptions();
     $custom = $parser->parse(head($lines));
     if ($custom) {
         $valid = true;
         foreach ($custom as $key => $value) {
             if (!array_key_exists($key, $options)) {
                 $valid = false;
                 break;
             }
         }
         if ($valid) {
             array_shift($lines);
             $options = $custom + $options;
         }
     }
     // Normalize the text back to a 0-level indent.
     $min_indent = 80;
     foreach ($lines as $line) {
         for ($ii = 0; $ii < strlen($line); $ii++) {
             if ($line[$ii] != ' ') {
                 $min_indent = min($ii, $min_indent);
                 break;
             }
         }
     }
     $text = implode("\n", $lines);
     if ($min_indent) {
         $indent_string = str_repeat(' ', $min_indent);
         $text = preg_replace('/^' . $indent_string . '/m', '', $text);
     }
     if ($this->getEngine()->isTextMode()) {
         $out = array();
         $header = array();
         if ($options['counterexample']) {
             $header[] = 'counterexample';
         }
         if ($options['name'] != '') {
             $header[] = 'name=' . $options['name'];
         }
         if ($header) {
             $out[] = implode(', ', $header);
         }
         $text = preg_replace('/^/m', '  ', $text);
         $out[] = $text;
         return implode("\n", $out);
     }
     if (empty($options['lang'])) {
         // If the user hasn't specified "lang=..." explicitly, try to guess the
         // language. If we fail, fall back to configured defaults.
         $lang = PhutilLanguageGuesser::guessLanguage($text);
         if (!$lang) {
             $lang = nonempty($this->getEngine()->getConfig('phutil.codeblock.language-default'), 'text');
         }
         $options['lang'] = $lang;
     }
     $code_body = $this->highlightSource($text, $options);
     $name_header = null;
     if ($this->getEngine()->isHTMLMailMode()) {
         $header_attributes = array('style' => 'padding: 6px 8px;
       background: #fdf5d4;
       color: rgba(0,0,0,.75);
       font-weight: bold;
       display: inline-block;
       border-top: 1px solid #f1c40f;
       border-left: 1px solid #f1c40f;
       border-right: 1px solid #f1c40f;
       margin-bottom: -1px;');
     } else {
         $header_attributes = array('class' => 'remarkup-code-header');
     }
     if ($options['name']) {
         $name_header = phutil_tag('div', $header_attributes, $options['name']);
     }
     $class = 'remarkup-code-block';
     if ($options['counterexample']) {
         $class = 'remarkup-code-block code-block-counterexample';
     }
     $attributes = array('class' => $class, 'data-code-lang' => $options['lang'], 'data-sigil' => 'remarkup-code-block');
     return phutil_tag('div', $attributes, array($name_header, $code_body));
 }
 public function markupText($text)
 {
     if (preg_match('/^```/', $text)) {
         // If this is a ```-style block, trim off the backticks.
         $text = preg_replace('/```\\s*$/', '', substr($text, 3));
     }
     $lines = explode("\n", $text);
     $options = array('counterexample' => false, 'lang' => null, 'name' => null, 'lines' => null);
     $parser = new PhutilSimpleOptions();
     $custom = $parser->parse(head($lines));
     if ($custom) {
         $valid = true;
         foreach ($custom as $key => $value) {
             if (!array_key_exists($key, $options)) {
                 $valid = false;
                 break;
             }
         }
         if ($valid) {
             array_shift($lines);
             $options = $custom + $options;
         }
     }
     // Normalize the text back to a 0-level indent.
     $min_indent = 80;
     foreach ($lines as $line) {
         for ($ii = 0; $ii < strlen($line); $ii++) {
             if ($line[$ii] != ' ') {
                 $min_indent = min($ii, $min_indent);
                 break;
             }
         }
     }
     $text = implode("\n", $lines);
     if ($min_indent) {
         $indent_string = str_repeat(' ', $min_indent);
         $text = preg_replace('/^' . $indent_string . '/m', '', $text);
     }
     if ($this->getEngine()->isTextMode()) {
         $out = array();
         $header = array();
         if ($options['counterexample']) {
             $header[] = 'counterexample';
         }
         if ($options['name'] != '') {
             $header[] = 'name=' . $options['name'];
         }
         if ($header) {
             $out[] = implode(', ', $header);
         }
         $text = preg_replace('/^/m', '  ', $text);
         $text = preg_replace('/^\\s+$/m', '', $text);
         $out[] = $text;
         return implode("\n", $out);
     }
     if (empty($options['lang'])) {
         // If the user hasn't specified "lang=..." explicitly, try to guess the
         // language. If we fail, fall back to configured defaults.
         $lang = PhutilLanguageGuesser::guessLanguage($text);
         if (!$lang) {
             $lang = nonempty($this->getEngine()->getConfig('phutil.codeblock.language-default'), 'php');
         }
         $options['lang'] = $lang;
     }
     $code_body = $this->highlightSource($text, $options);
     $name_header = null;
     if ($options['name']) {
         $name_header = phutil_tag('div', array('class' => 'remarkup-code-header'), $options['name']);
     }
     return phutil_tag('div', array('class' => 'remarkup-code-block', 'data-code-lang' => $options['lang']), array($name_header, $code_body));
 }
 public function markupEmbedFile($matches)
 {
     $file = null;
     if ($matches[1]) {
         // TODO: This is pretty inefficient if there are a bunch of files.
         $file = id(new PhabricatorFile())->load($matches[1]);
     }
     if (!$file) {
         return $matches[0];
     }
     $options = array('size' => 'thumb', 'layout' => 'left', 'float' => false, 'name' => null);
     if (!empty($matches[2])) {
         $matches[2] = trim($matches[2], ', ');
         $options = PhutilSimpleOptions::parse($matches[2]) + $options;
     }
     $file_name = coalesce($options['name'], $file->getName());
     if (!$file->isViewableImage() || $options['layout'] == 'link') {
         // If a file isn't in image, just render a link to it.
         $link = phutil_render_tag('a', array('href' => $file->getBestURI(), 'target' => '_blank', 'class' => 'phabricator-remarkup-embed-layout-link'), phutil_escape_html($file_name));
         return $this->getEngine()->storeText($link);
     }
     $attrs = array();
     switch ($options['size']) {
         case 'full':
             $attrs['src'] = $file->getBestURI();
             $link = null;
             break;
         case 'thumb':
         default:
             $attrs['src'] = $file->getPreview220URI();
             $link = $file->getBestURI();
             break;
     }
     $embed = phutil_render_tag('img', $attrs);
     if ($link) {
         $embed = phutil_render_tag('a', array('href' => $link, 'class' => 'phabricator-remarkup-embed-image', 'target' => '_blank'), $embed);
     }
     $layout_class = null;
     switch ($options['layout']) {
         case 'right':
         case 'center':
         case 'inline':
         case 'left':
             $layout_class = 'phabricator-remarkup-embed-layout-' . $options['layout'];
             break;
         default:
             $layout_class = 'phabricator-remarkup-embed-layout-left';
             break;
     }
     if ($options['float']) {
         switch ($options['layout']) {
             case 'center':
             case 'inline':
                 break;
             case 'right':
                 $layout_class .= ' phabricator-remarkup-embed-float-right';
                 break;
             case 'left':
             default:
                 $layout_class .= ' phabricator-remarkup-embed-float-left';
                 break;
         }
     }
     if ($layout_class) {
         $embed = phutil_render_tag('div', array('class' => $layout_class), $embed);
     }
     return $this->getEngine()->storeText($embed);
 }
 public function markupText($text)
 {
     if (preg_match('/^```/', $text)) {
         // If this is a ```-style block, trim off the backticks.
         $text = preg_replace('/```\\s*$/', '', substr($text, 3));
     }
     $lines = explode("\n", $text);
     $options = array('counterexample' => false, 'lang' => null, 'name' => null, 'lines' => null);
     $custom = PhutilSimpleOptions::parse(head($lines));
     if ($custom) {
         $valid = true;
         foreach ($custom as $key => $value) {
             if (!array_key_exists($key, $options)) {
                 $valid = false;
                 break;
             }
         }
         if ($valid) {
             array_shift($lines);
             $options = $custom + $options;
         }
     }
     if ($options['counterexample']) {
         $aux_class = ' remarkup-counterexample';
     } else {
         $aux_class = null;
     }
     // Normalize the text back to a 0-level indent.
     $min_indent = 80;
     foreach ($lines as $line) {
         for ($ii = 0; $ii < strlen($line); $ii++) {
             if ($line[$ii] != ' ') {
                 $min_indent = min($ii, $min_indent);
                 break;
             }
         }
     }
     if ($min_indent) {
         $indent_string = str_repeat(' ', $min_indent);
         $text = preg_replace('/^' . $indent_string . '/m', '', implode("\n", $lines));
     } else {
         $text = implode("\n", $lines);
     }
     if (empty($options['lang'])) {
         // If the user hasn't specified "lang=..." explicitly, try to guess the
         // language. If we fail, fall back to configured defaults.
         $lang = PhutilLanguageGuesser::guessLanguage($text);
         if (!$lang) {
             $lang = nonempty($this->getEngine()->getConfig('phutil.codeblock.language-default'), 'php');
         }
         $options['lang'] = $lang;
     }
     $name_header = null;
     if ($options['name']) {
         $name_header = phutil_render_tag('div', array('class' => 'remarkup-code-header'), phutil_escape_html($options['name']));
     }
     $aux_style = null;
     if ($options['lines']) {
         // Put a minimum size on this because the scrollbar is otherwise
         // unusable.
         $height = max(6, (int) $options['lines']);
         $aux_style = 'max-height: ' . 2 * $height . 'em;';
     }
     $engine = $this->getEngine()->getConfig('syntax-highlighter.engine');
     if (!$engine) {
         $engine = 'PhutilDefaultSyntaxHighlighterEngine';
     }
     $engine = newv($engine, array());
     $engine->setConfig('pygments.enabled', $this->getEngine()->getConfig('pygments.enabled'));
     $code_body = phutil_render_tag('pre', array('class' => 'remarkup-code' . $aux_class, 'style' => $aux_style), $engine->highlightSource($options['lang'], $text));
     return phutil_render_tag('div', array('class' => 'remarkup-code-block', 'data-code-lang' => $options['lang']), $name_header . $code_body);
 }
예제 #21
0
 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;
     }
 }