[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/differential/controller/ -> DifferentialChangesetViewController.php (source)

   1  <?php
   2  
   3  final class DifferentialChangesetViewController extends DifferentialController {
   4  
   5    public function shouldAllowPublic() {
   6      return true;
   7    }
   8  
   9    public function processRequest() {
  10      $request = $this->getRequest();
  11  
  12      $author_phid = $request->getUser()->getPHID();
  13  
  14      $rendering_reference = $request->getStr('ref');
  15      $parts = explode('/', $rendering_reference);
  16      if (count($parts) == 2) {
  17        list($id, $vs) = $parts;
  18      } else {
  19        $id = $parts[0];
  20        $vs = 0;
  21      }
  22  
  23      $id = (int)$id;
  24      $vs = (int)$vs;
  25  
  26      $load_ids = array($id);
  27      if ($vs && ($vs != -1)) {
  28        $load_ids[] = $vs;
  29      }
  30  
  31      $changesets = id(new DifferentialChangesetQuery())
  32        ->setViewer($request->getUser())
  33        ->withIDs($load_ids)
  34        ->needHunks(true)
  35        ->execute();
  36      $changesets = mpull($changesets, null, 'getID');
  37  
  38      $changeset = idx($changesets, $id);
  39      if (!$changeset) {
  40        return new Aphront404Response();
  41      }
  42  
  43      $vs_changeset = null;
  44      if ($vs && ($vs != -1)) {
  45        $vs_changeset = idx($changesets, $vs);
  46        if (!$vs_changeset) {
  47          return new Aphront404Response();
  48        }
  49      }
  50  
  51      $view = $request->getStr('view');
  52      if ($view) {
  53        $phid = idx($changeset->getMetadata(), "$view:binary-phid");
  54        if ($phid) {
  55          return id(new AphrontRedirectResponse())->setURI("/file/info/$phid/");
  56        }
  57        switch ($view) {
  58          case 'new':
  59            return $this->buildRawFileResponse($changeset, $is_new = true);
  60          case 'old':
  61            if ($vs_changeset) {
  62              return $this->buildRawFileResponse($vs_changeset, $is_new = true);
  63            }
  64            return $this->buildRawFileResponse($changeset, $is_new = false);
  65          default:
  66            return new Aphront400Response();
  67        }
  68      }
  69  
  70      if (!$vs) {
  71        $right = $changeset;
  72        $left  = null;
  73  
  74        $right_source = $right->getID();
  75        $right_new = true;
  76        $left_source = $right->getID();
  77        $left_new = false;
  78  
  79        $render_cache_key = $right->getID();
  80      } else if ($vs == -1) {
  81        $right = null;
  82        $left = $changeset;
  83  
  84        $right_source = $left->getID();
  85        $right_new = false;
  86        $left_source = $left->getID();
  87        $left_new = true;
  88  
  89        $render_cache_key = null;
  90      } else {
  91        $right = $changeset;
  92        $left = $vs_changeset;
  93  
  94        $right_source = $right->getID();
  95        $right_new = true;
  96        $left_source = $left->getID();
  97        $left_new = true;
  98  
  99        $render_cache_key = null;
 100      }
 101  
 102      if ($left) {
 103        $left_data = $left->makeNewFile();
 104        if ($right) {
 105          $right_data = $right->makeNewFile();
 106        } else {
 107          $right_data = $left->makeOldFile();
 108        }
 109  
 110        $engine = new PhabricatorDifferenceEngine();
 111        $synthetic = $engine->generateChangesetFromFileContent(
 112          $left_data,
 113          $right_data);
 114  
 115        $choice = clone nonempty($left, $right);
 116        $choice->attachHunks($synthetic->getHunks());
 117  
 118        $changeset = $choice;
 119      }
 120  
 121      $coverage = null;
 122      if ($right && $right->getDiffID()) {
 123        $unit = id(new DifferentialDiffProperty())->loadOneWhere(
 124          'diffID = %d AND name = %s',
 125          $right->getDiffID(),
 126          'arc:unit');
 127  
 128        if ($unit) {
 129          $coverage = array();
 130          foreach ($unit->getData() as $result) {
 131            $result_coverage = idx($result, 'coverage');
 132            if (!$result_coverage) {
 133              continue;
 134            }
 135            $file_coverage = idx($result_coverage, $right->getFileName());
 136            if (!$file_coverage) {
 137              continue;
 138            }
 139            $coverage[] = $file_coverage;
 140          }
 141  
 142          $coverage = ArcanistUnitTestResult::mergeCoverage($coverage);
 143        }
 144      }
 145  
 146      $spec = $request->getStr('range');
 147      list($range_s, $range_e, $mask) =
 148        DifferentialChangesetParser::parseRangeSpecification($spec);
 149  
 150      $parser = new DifferentialChangesetParser();
 151      $parser->setCoverage($coverage);
 152      $parser->setChangeset($changeset);
 153      $parser->setRenderingReference($rendering_reference);
 154      $parser->setRenderCacheKey($render_cache_key);
 155      $parser->setRightSideCommentMapping($right_source, $right_new);
 156      $parser->setLeftSideCommentMapping($left_source, $left_new);
 157      $parser->setWhitespaceMode($request->getStr('whitespace'));
 158      $parser->setCharacterEncoding($request->getStr('encoding'));
 159      $parser->setHighlightAs($request->getStr('highlight'));
 160  
 161      if ($request->getStr('renderer') == '1up') {
 162        $parser->setRenderer(new DifferentialChangesetOneUpRenderer());
 163      }
 164  
 165  
 166      if ($left && $right) {
 167        $parser->setOriginals($left, $right);
 168      }
 169  
 170      // Load both left-side and right-side inline comments.
 171      $inlines = $this->loadInlineComments(
 172        array($left_source, $right_source),
 173        $author_phid);
 174  
 175      if ($left_new) {
 176        $inlines = array_merge(
 177          $inlines,
 178          $this->buildLintInlineComments($left));
 179      }
 180  
 181      if ($right_new) {
 182        $inlines = array_merge(
 183          $inlines,
 184          $this->buildLintInlineComments($right));
 185      }
 186  
 187      $phids = array();
 188      foreach ($inlines as $inline) {
 189        $parser->parseInlineComment($inline);
 190        if ($inline->getAuthorPHID()) {
 191          $phids[$inline->getAuthorPHID()] = true;
 192        }
 193      }
 194      $phids = array_keys($phids);
 195  
 196      $handles = $this->loadViewerHandles($phids);
 197      $parser->setHandles($handles);
 198  
 199      $engine = new PhabricatorMarkupEngine();
 200      $engine->setViewer($request->getUser());
 201  
 202      foreach ($inlines as $inline) {
 203        $engine->addObject(
 204          $inline,
 205          PhabricatorInlineCommentInterface::MARKUP_FIELD_BODY);
 206      }
 207  
 208      $engine->process();
 209      $parser->setMarkupEngine($engine);
 210  
 211      if ($request->isAjax()) {
 212        // TODO: This is sort of lazy, the effect is just to not render "Edit"
 213        // and "Reply" links on the "standalone view".
 214        $parser->setUser($request->getUser());
 215      }
 216  
 217      $output = $parser->render($range_s, $range_e, $mask);
 218  
 219      $mcov = $parser->renderModifiedCoverage();
 220  
 221      if ($request->isAjax()) {
 222        $coverage = array(
 223          'differential-mcoverage-'.md5($changeset->getFilename()) => $mcov,
 224        );
 225  
 226        return id(new PhabricatorChangesetResponse())
 227          ->setRenderedChangeset($output)
 228          ->setCoverage($coverage);
 229      }
 230  
 231      Javelin::initBehavior('differential-show-more', array(
 232        'uri' => '/differential/changeset/',
 233        'whitespace' => $request->getStr('whitespace'),
 234      ));
 235  
 236      Javelin::initBehavior('differential-comment-jump', array());
 237  
 238      // TODO: [HTML] Clean up DifferentialChangesetParser output, but it's
 239      // undergoing like six kinds of refactoring anyway.
 240      $output = phutil_safe_html($output);
 241  
 242      $detail = new DifferentialChangesetDetailView();
 243      $detail->setChangeset($changeset);
 244      $detail->appendChild($output);
 245      $detail->setVsChangesetID($left_source);
 246  
 247      $panel = new DifferentialPrimaryPaneView();
 248      $panel->appendChild(
 249        phutil_tag(
 250        'div',
 251        array(
 252          'class' => 'differential-review-stage',
 253          'id'    => 'differential-review-stage',
 254        ),
 255        $detail->render()));
 256  
 257      $crumbs = $this->buildApplicationCrumbs();
 258  
 259      $revision_id = $changeset->getDiff()->getRevisionID();
 260      if ($revision_id) {
 261        $crumbs->addTextCrumb('D'.$revision_id, '/D'.$revision_id);
 262      }
 263  
 264      $diff_id = $changeset->getDiff()->getID();
 265      if ($diff_id) {
 266        $crumbs->addTextCrumb(
 267          pht('Diff %d', $diff_id),
 268          $this->getApplicationURI('diff/'.$diff_id));
 269      }
 270  
 271      $crumbs->addTextCrumb($changeset->getDisplayFilename());
 272  
 273      $box = id(new PHUIObjectBoxView())
 274        ->setHeaderText(pht('Standalone View'))
 275        ->appendChild($panel);
 276  
 277      return $this->buildApplicationPage(
 278        array(
 279          $crumbs,
 280          $box,
 281        ),
 282        array(
 283          'title' => pht('Changeset View'),
 284          'device' => false,
 285        ));
 286    }
 287  
 288    private function loadInlineComments(array $changeset_ids, $author_phid) {
 289      $changeset_ids = array_unique(array_filter($changeset_ids));
 290      if (!$changeset_ids) {
 291        return;
 292      }
 293  
 294      return id(new DifferentialInlineCommentQuery())
 295        ->withViewerAndChangesetIDs($author_phid, $changeset_ids)
 296        ->execute();
 297    }
 298  
 299    private function buildRawFileResponse(
 300      DifferentialChangeset $changeset,
 301      $is_new) {
 302  
 303      $viewer = $this->getRequest()->getUser();
 304  
 305      if ($is_new) {
 306        $key = 'raw:new:phid';
 307      } else {
 308        $key = 'raw:old:phid';
 309      }
 310  
 311      $metadata = $changeset->getMetadata();
 312  
 313      $file = null;
 314      $phid = idx($metadata, $key);
 315      if ($phid) {
 316        $file = id(new PhabricatorFileQuery())
 317          ->setViewer($viewer)
 318          ->withPHIDs(array($phid))
 319          ->execute();
 320        if ($file) {
 321          $file = head($file);
 322        }
 323      }
 324  
 325      if (!$file) {
 326        // This is just building a cache of the changeset content in the file
 327        // tool, and is safe to run on a read pathway.
 328        $unguard = AphrontWriteGuard::beginScopedUnguardedWrites();
 329  
 330        if ($is_new) {
 331          $data = $changeset->makeNewFile();
 332        } else {
 333          $data = $changeset->makeOldFile();
 334        }
 335  
 336        $file = PhabricatorFile::newFromFileData(
 337          $data,
 338          array(
 339            'name'      => $changeset->getFilename(),
 340            'mime-type' => 'text/plain',
 341          ));
 342  
 343        $metadata[$key] = $file->getPHID();
 344        $changeset->setMetadata($metadata);
 345        $changeset->save();
 346  
 347        unset($unguard);
 348      }
 349  
 350      return $file->getRedirectResponse();
 351    }
 352  
 353    private function buildLintInlineComments($changeset) {
 354      $lint = id(new DifferentialDiffProperty())->loadOneWhere(
 355        'diffID = %d AND name = %s',
 356        $changeset->getDiffID(),
 357        'arc:lint');
 358      if (!$lint) {
 359        return array();
 360      }
 361      $lint = $lint->getData();
 362  
 363      $inlines = array();
 364      foreach ($lint as $msg) {
 365        if ($msg['path'] != $changeset->getFilename()) {
 366          continue;
 367        }
 368        $inline = new DifferentialInlineComment();
 369        $inline->setChangesetID($changeset->getID());
 370        $inline->setIsNewFile(1);
 371        $inline->setSyntheticAuthor('Lint: '.$msg['name']);
 372        $inline->setLineNumber($msg['line']);
 373        $inline->setLineLength(0);
 374  
 375        $inline->setContent('%%%'.$msg['description'].'%%%');
 376  
 377        $inlines[] = $inline;
 378      }
 379  
 380      return $inlines;
 381    }
 382  
 383  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1