diff --git a/src/applications/diffusion/controller/DiffusionLintController.php b/src/applications/diffusion/controller/DiffusionLintController.php index 585bc7b27..dc4471b97 100644 --- a/src/applications/diffusion/controller/DiffusionLintController.php +++ b/src/applications/diffusion/controller/DiffusionLintController.php @@ -1,228 +1,227 @@ getRequest(); $user = $this->getRequest()->getUser(); $drequest = $this->diffusionRequest; if ($request->getStr('lint') !== null) { $controller = new DiffusionLintDetailsController($request); $controller->setDiffusionRequest($drequest); return $this->delegateToController($controller); } $owners = array(); if (!$drequest) { if (!$request->getArr('owner')) { $owners[$user->getPHID()] = $user->getFullName(); } else { $phids = $request->getArr('owner'); $phid = reset($phids); $handles = $this->loadViewerHandles(array($phid)); $owners[$phid] = $handles[$phid]->getFullName(); } } $codes = $this->loadLintCodes(array_keys($owners)); if ($codes && !$drequest) { $branches = id(new PhabricatorRepositoryBranch())->loadAllWhere( 'id IN (%Ld)', array_unique(ipull($codes, 'branchID'))); $repositories = id(new PhabricatorRepository())->loadAllWhere( 'id IN (%Ld)', array_unique(mpull($branches, 'getRepositoryID'))); $drequests = array(); foreach ($branches as $id => $branch) { $drequests[$id] = DiffusionRequest::newFromDictionary(array( 'repository' => $repositories[$branch->getRepositoryID()], 'branch' => $branch->getName(), )); } } $rows = array(); foreach ($codes as $code) { if (!$this->diffusionRequest) { $drequest = $drequests[$code['branchID']]; } $rows[] = array( hsprintf( '%s', $drequest->generateURI(array( 'action' => 'lint', 'lint' => $code['code'], )), $code['n']), hsprintf( '%s', $drequest->generateURI(array( 'action' => 'browse', 'lint' => $code['code'], )), $code['files']), hsprintf( '%s', $drequest->generateURI(array('action' => 'lint')), $drequest->getCallsign()), phutil_escape_html(ArcanistLintSeverity::getStringForSeverity( $code['maxSeverity'])), phutil_escape_html($code['code']), phutil_escape_html($code['maxName']), phutil_escape_html($code['maxDescription']), ); } $table = id(new AphrontTableView($rows)) ->setHeaders(array( 'Problems', 'Files', 'Repository', 'Severity', 'Code', 'Name', 'Example', )) ->setColumnVisibility(array(true, true, !$this->diffusionRequest)) ->setColumnClasses(array('n', 'n', '', '', 'pri', '', '')); $content = array(); $content[] = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'lint', )); $link = null; if ($this->diffusionRequest) { $link = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'lint', 'lint' => '', )), pht('Switch to List View')); } else { $form = id(new AphrontFormView()) ->setUser($user) ->setMethod('GET') ->appendChild( id(new AphrontFormTokenizerControl()) ->setDatasource('/typeahead/common/users/') ->setLimit(1) ->setName('owner') ->setLabel('Owner') ->setValue($owners)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue('Filter')); $content[] = id(new AphrontListFilterView())->appendChild($form); } $content[] = id(new AphrontPanelView()) ->setHeader(pht('%d Lint Message(s)', array_sum(ipull($codes, 'n')))) ->setCaption($link) ->appendChild($table); $title = array('Lint'); if ($this->diffusionRequest) { $title[] = $drequest->getCallsign(); $content = $this->buildSideNav('lint', false)->appendChild($content); } return $this->buildStandardPageResponse( $content, array('title' => $title)); } private function loadLintCodes(array $owner_phids) { $drequest = $this->diffusionRequest; $conn = id(new PhabricatorRepository())->establishConnection('r'); $where = array('1 = 1'); if ($drequest) { $branch = $drequest->loadBranch(); if (!$branch) { return array(); } $where[] = qsprintf($conn, 'branchID = %d', $branch->getID()); if ($drequest->getPath() != '') { $is_dir = (substr($drequest->getPath(), -1) == '/'); $where[] = qsprintf( $conn, 'path '.($is_dir ? 'LIKE %>' : '= %s'), '/'.$drequest->getPath()); } } if ($owner_phids) { $packages = id(new PhabricatorOwnersOwner()) ->loadAllWhere('userPHID IN (%Ls)', $owner_phids); if (!$packages) { return array(); } $paths = id(new PhabricatorOwnersPath()) ->loadAllWhere('packageID IN (%Ld)', array_keys($packages)); if (!$paths) { return array(); } $repositories = id(new PhabricatorRepository())->loadAllWhere( 'phid IN (%Ls)', array_unique(mpull($paths, 'getRepositoryPHID'))); $repositories = mpull($repositories, 'getID', 'getPHID'); $branches = id(new PhabricatorRepositoryBranch())->loadAllWhere( 'repositoryID IN (%Ld)', $repositories); $branches = mgroup($branches, 'getRepositoryID'); $or = array(); foreach ($paths as $path) { $branch = idx($branches, $repositories[$path->getRepositoryPHID()]); if ($branch) { $or[] = qsprintf( $conn, '(branchID IN (%Ld) AND path LIKE %>)', array_keys($branch), $path->getPath()); } } if (!$or) { return array(); } $where[] = '('.implode(' OR ', $or).')'; } return queryfx_all( $conn, 'SELECT branchID, code, MAX(severity) AS maxSeverity, MAX(name) AS maxName, MAX(description) AS maxDescription, COUNT(DISTINCT path) AS files, COUNT(*) AS n FROM %T WHERE %Q GROUP BY branchID, code ORDER BY n DESC', PhabricatorRepository::TABLE_LINTMESSAGE, implode(' AND ', $where)); } } diff --git a/src/applications/diffusion/controller/DiffusionLintDetailsController.php b/src/applications/diffusion/controller/DiffusionLintDetailsController.php index c8cc96901..355958459 100644 --- a/src/applications/diffusion/controller/DiffusionLintDetailsController.php +++ b/src/applications/diffusion/controller/DiffusionLintDetailsController.php @@ -1,142 +1,141 @@ getRequest()->getInt('offset', 0); $drequest = $this->getDiffusionRequest(); $branch = $drequest->loadBranch(); $messages = $this->loadLintMessages($branch, $limit, $offset); $is_dir = (substr('/'.$drequest->getPath(), -1) == '/'); $rows = array(); foreach ($messages as $message) { $path = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'lint', 'path' => $message['path'], )), substr($message['path'], strlen($drequest->getPath()) + 1)); $line = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'browse', 'path' => $message['path'], 'line' => $message['line'], 'commit' => $branch->getLintCommit(), )), $message['line']); $rows[] = array( $path, $line, phutil_escape_html(ArcanistLintSeverity::getStringForSeverity( $message['severity'])), phutil_escape_html($message['name']), phutil_escape_html($message['description']), ); } $table = id(new AphrontTableView($rows)) ->setHeaders(array( 'Path', 'Line', 'Severity', 'Name', 'Description', )) ->setColumnClasses(array('', 'n', '', '', '')) ->setColumnVisibility(array($is_dir)); $content = array(); $content[] = $this->buildCrumbs( array( 'branch' => true, 'path' => true, 'view' => 'lint', )); $pager = id(new AphrontPagerView()) ->setPageSize($limit) ->setOffset($offset) ->setHasMorePages(count($messages) >= $limit) ->setURI($this->getRequest()->getRequestURI(), 'offset'); $lint = $drequest->getLint(); $link = hsprintf( '%s', $drequest->generateURI(array( 'action' => 'lint', 'lint' => null, )), pht('Switch to Grouped View')); $content[] = id(new AphrontPanelView()) ->setHeader( ($lint != '' ? phutil_escape_html($lint)." \xC2\xB7 " : ''). pht('%d Lint Message(s)', count($messages))) ->setCaption($link) ->appendChild($table) ->appendChild($pager); $nav = $this->buildSideNav('lint', false); $nav->appendChild($content); return $this->buildStandardPageResponse( $nav, array('title' => array( 'Lint', $drequest->getRepository()->getCallsign(), ))); } private function loadLintMessages( PhabricatorRepositoryBranch $branch, $limit, $offset) { $drequest = $this->getDiffusionRequest(); if (!$branch) { return array(); } $conn = $branch->establishConnection('r'); $where = array(); if ($drequest->getPath() != '') { $is_dir = (substr($drequest->getPath(), -1) == '/'); $where[] = qsprintf( $conn, 'path '.($is_dir ? 'LIKE %>' : '= %s'), '/'.$drequest->getPath()); } if ($drequest->getLint() != '') { $where[] = qsprintf( $conn, 'code = %s', $drequest->getLint()); } return queryfx_all( $conn, 'SELECT * FROM %T WHERE branchID = %d AND %Q ORDER BY path, code, line LIMIT %d OFFSET %d', PhabricatorRepository::TABLE_LINTMESSAGE, $branch->getID(), implode(' AND ', $where), $limit, $offset); } } diff --git a/src/applications/pholio/application/PhabricatorApplicationPholio.php b/src/applications/pholio/application/PhabricatorApplicationPholio.php index 83e754ea8..742aab946 100644 --- a/src/applications/pholio/application/PhabricatorApplicationPholio.php +++ b/src/applications/pholio/application/PhabricatorApplicationPholio.php @@ -1,67 +1,51 @@ [1-9]\d*)' => 'PholioMockViewController', '/pholio/' => array( '' => 'PholioMockListController', 'view/(?P\w+)/' => 'PholioMockListController', 'new/' => 'PholioMockEditController', 'edit/(?P\d+)/' => 'PholioMockEditController', 'comment/(?P\d+)/' => 'PholioMockCommentController', ), ); } } diff --git a/src/applications/pholio/constants/PholioConstants.php b/src/applications/pholio/constants/PholioConstants.php index 40b133558..387a82337 100644 --- a/src/applications/pholio/constants/PholioConstants.php +++ b/src/applications/pholio/constants/PholioConstants.php @@ -1,21 +1,5 @@ setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->addLabel('Create'); $nav->addFilter('new', pht('Create Mock')); $nav->addLabel('Mocks'); $nav->addFilter('view/all', pht('All Mocks')); return $nav; } } diff --git a/src/applications/pholio/controller/PholioMockCommentController.php b/src/applications/pholio/controller/PholioMockCommentController.php index 31d1c798b..b61502a61 100644 --- a/src/applications/pholio/controller/PholioMockCommentController.php +++ b/src/applications/pholio/controller/PholioMockCommentController.php @@ -1,74 +1,58 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $mock = id(new PholioMockQuery()) ->setViewer($user) ->withIDs(array($this->id)) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $mock_uri = '/M'.$mock->getID(); $comment = $request->getStr('comment'); if (!strlen($comment)) { $dialog = id(new AphrontDialogView()) ->setUser($user) ->setTitle(pht('Empty Comment')) ->appendChild('You did not provide a comment!') ->addCancelButton($mock_uri); return id(new AphrontDialogResponse())->setDialog($dialog); } $content_source = PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_WEB, array( 'ip' => $request->getRemoteAddr(), )); $xaction = id(new PholioTransaction()) ->setTransactionType(PholioTransactionType::TYPE_NONE) ->setComment($comment); id(new PholioMockEditor()) ->setActor($user) ->setContentSource($content_source) ->applyTransactions($mock, array($xaction)); return id(new AphrontRedirectResponse())->setURI($mock_uri); } } diff --git a/src/applications/pholio/controller/PholioMockEditController.php b/src/applications/pholio/controller/PholioMockEditController.php index da4285b39..1118cba58 100644 --- a/src/applications/pholio/controller/PholioMockEditController.php +++ b/src/applications/pholio/controller/PholioMockEditController.php @@ -1,232 +1,216 @@ id = idx($data, 'id'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); if ($this->id) { $mock = id(new PholioMockQuery()) ->setViewer($user) ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, )) ->withIDs(array($this->id)) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $title = pht('Edit Mock'); $is_new = false; } else { $mock = new PholioMock(); $mock->setAuthorPHID($user->getPHID()); $mock->setViewPolicy(PhabricatorPolicies::POLICY_USER); $title = pht('Create Mock'); $is_new = true; } $e_name = true; $e_images = true; $errors = array(); $v_name = $mock->getName(); $v_desc = $mock->getDescription(); $v_view = $mock->getViewPolicy(); if ($request->isFormPost()) { $xactions = array(); $type_name = PholioTransactionType::TYPE_NAME; $type_desc = PholioTransactionType::TYPE_DESCRIPTION; $type_view = PholioTransactionType::TYPE_VIEW_POLICY; $v_name = $request->getStr('name'); $v_desc = $request->getStr('description'); $v_view = $request->getStr('can_view'); $xactions[$type_name] = $v_name; $xactions[$type_desc] = $v_desc; $xactions[$type_view] = $v_view; if (!strlen($request->getStr('name'))) { $e_name = 'Required'; $errors[] = pht('You must name the mock.'); } $images = array(); if ($is_new) { // TODO: Make this transactional and allow edits? $files = array(); $file_phids = $request->getArr('file_phids'); if ($file_phids) { $files = id(new PhabricatorFileQuery()) ->setViewer($user) ->withPHIDs($file_phids) ->execute(); } if (!$files) { $e_images = 'Required'; $errors[] = pht('You must add at least one image to the mock.'); } else { $mock->setCoverPHID(head($files)->getPHID()); } $sequence = 0; foreach ($files as $file) { $image = new PholioImage(); $image->setFilePHID($file->getPHID()); $image->setSequence($sequence++); $images[] = $image; } } if (!$errors) { $content_source = PhabricatorContentSource::newForSource( PhabricatorContentSource::SOURCE_WEB, array( 'ip' => $request->getRemoteAddr(), )); foreach ($xactions as $type => $value) { $xactions[$type] = id(new PholioTransaction()) ->setTransactionType($type) ->setNewValue($value); } $mock->openTransaction(); $editor = id(new PholioMockEditor()) ->setContentSource($content_source) ->setActor($user); $editor->applyTransactions($mock, $xactions); if ($images) { foreach ($images as $image) { // TODO: Move into editor? $image->setMockID($mock->getID()); $image->save(); } } $mock->saveTransaction(); return id(new AphrontRedirectResponse()) ->setURI('/M'.$mock->getID()); } } if ($errors) { $error_view = id(new AphrontErrorView()) ->setTitle(pht('Form Errors')) ->setErrors($errors); } else { $error_view = null; } if ($this->id) { $submit = id(new AphrontFormSubmitControl()) ->addCancelButton('/M'.$this->id) ->setValue(pht('Save')); } else { $submit = id(new AphrontFormSubmitControl()) ->addCancelButton($this->getApplicationURI()) ->setValue(pht('Create')); } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($user) ->setObject($mock) ->execute(); // NOTE: Make this show up correctly on the rendered form. $mock->setViewPolicy($v_view); $form = id(new AphrontFormView()) ->setUser($user) ->setFlexible(true) ->appendChild( id(new AphrontFormTextControl()) ->setName('name') ->setValue($v_name) ->setLabel(pht('Name')) ->setError($e_name)) ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('description') ->setValue($v_desc) ->setLabel(pht('Description')) ->setUser($user)) ->appendChild( id(new AphrontFormDragAndDropUploadControl($request)) ->setName('file_phids') ->setLabel(pht('Images')) ->setError($e_images)) ->appendChild( id(new AphrontFormPolicyControl()) ->setUser($user) ->setCapability(PhabricatorPolicyCapability::CAN_VIEW) ->setPolicyObject($mock) ->setPolicies($policies) ->setName('can_view')) ->appendChild($submit); $header = id(new PhabricatorHeaderView()) ->setHeader($title); $content = array( $header, $error_view, $form, ); $nav = $this->buildSideNav(); $nav->selectFilter(null); $nav->appendChild($content); return $this->buildApplicationPage( $nav, array( 'title' => $title, )); } } diff --git a/src/applications/pholio/controller/PholioMockListController.php b/src/applications/pholio/controller/PholioMockListController.php index 272f44c6e..943eab946 100644 --- a/src/applications/pholio/controller/PholioMockListController.php +++ b/src/applications/pholio/controller/PholioMockListController.php @@ -1,78 +1,62 @@ view = idx($data, 'view'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $query = id(new PholioMockQuery()) ->setViewer($user); $nav = $this->buildSideNav(); $filter = $nav->selectFilter('view/'.$this->view, 'view/all'); switch ($filter) { case 'view/all': default: $title = 'All Mocks'; break; } $pager = new AphrontCursorPagerView(); $pager->readFromRequest($request); $mocks = $query->executeWithCursorPager($pager); $board = new PhabricatorPinboardView(); foreach ($mocks as $mock) { $board->addItem( id(new PhabricatorPinboardItemView()) ->setHeader($mock->getName()) ->setURI('/M'.$mock->getID())); } $header = id(new PhabricatorHeaderView()) ->setHeader($title); $content = array( $header, $board, $pager, ); $nav->appendChild($content); return $this->buildApplicationPage( $nav, array( 'title' => $title, )); } } diff --git a/src/applications/pholio/controller/PholioMockViewController.php b/src/applications/pholio/controller/PholioMockViewController.php index 0d9b93816..e21c1ac4f 100644 --- a/src/applications/pholio/controller/PholioMockViewController.php +++ b/src/applications/pholio/controller/PholioMockViewController.php @@ -1,333 +1,317 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $mock = id(new PholioMockQuery()) ->setViewer($user) ->withIDs(array($this->id)) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $xactions = id(new PholioTransactionQuery()) ->withMockIDs(array($mock->getID())) ->execute(); $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( $mock->getPHID()); $phids = array(); $phids[] = $mock->getAuthorPHID(); foreach ($xactions as $xaction) { $phids[] = $xaction->getAuthorPHID(); foreach ($xaction->getRequiredHandlePHIDs() as $hphid) { $phids[] = $hphid; } } foreach ($subscribers as $subscriber) { $phids[] = $subscriber; } $this->loadHandles($phids); $engine = id(new PhabricatorMarkupEngine()) ->setViewer($user); $engine->addObject($mock, PholioMock::MARKUP_FIELD_DESCRIPTION); foreach ($xactions as $xaction) { $engine->addObject($xaction, PholioTransaction::MARKUP_FIELD_COMMENT); } $engine->process(); $title = 'M'.$mock->getID().' '.$mock->getName(); $header = id(new PhabricatorHeaderView()) ->setHeader($title); $actions = $this->buildActionView($mock); $properties = $this->buildPropertyView($mock, $engine, $subscribers); $carousel = '

'. 'Carousel Goes Here

'; $xaction_view = $this->buildTransactionView($xactions, $engine); $add_comment = $this->buildAddCommentView($mock); $content = array( $header, $actions, $properties, $carousel, $xaction_view, $add_comment, ); return $this->buildApplicationPage( $content, array( 'title' => $title, 'device' => true, )); } private function buildActionView(PholioMock $mock) { $user = $this->getRequest()->getUser(); $actions = id(new PhabricatorActionListView()) ->setUser($user) ->setObject($mock); $can_edit = PhabricatorPolicyFilter::hasCapability( $user, $mock, PhabricatorPolicyCapability::CAN_EDIT); $actions->addAction( id(new PhabricatorActionView()) ->setIcon('edit') ->setName(pht('Edit Mock')) ->setHref($this->getApplicationURI('/edit/'.$mock->getID())) ->setDisabled(!$can_edit) ->setWorkflow(!$can_edit)); return $actions; } private function buildPropertyView( PholioMock $mock, PhabricatorMarkupEngine $engine, array $subscribers) { $user = $this->getRequest()->getUser(); $properties = new PhabricatorPropertyListView(); $properties->addProperty( pht('Author'), $this->getHandle($mock->getAuthorPHID())->renderLink()); $properties->addProperty( pht('Created'), phabricator_datetime($mock->getDateCreated(), $user)); $descriptions = PhabricatorPolicyQuery::renderPolicyDescriptions( $user, $mock); $properties->addProperty( pht('Visible To'), $descriptions[PhabricatorPolicyCapability::CAN_VIEW]); if ($subscribers) { $sub_view = array(); foreach ($subscribers as $subscriber) { $sub_view[] = $this->getHandle($subscriber)->renderLink(); } $sub_view = implode(', ', $sub_view); } else { $sub_view = ''.pht('None').''; } $properties->addProperty( pht('Subscribers'), $sub_view); $properties->addTextContent( $engine->getOutput($mock, PholioMock::MARKUP_FIELD_DESCRIPTION)); return $properties; } private function buildAddCommentView(PholioMock $mock) { $user = $this->getRequest()->getUser(); $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $title = $is_serious ? pht('Add Comment') : pht('History Beckons'); $header = id(new PhabricatorHeaderView()) ->setHeader($title); $action = $is_serious ? pht('Add Comment') : pht('Answer The Call'); $form = id(new AphrontFormView()) ->setUser($user) ->setAction($this->getApplicationURI('/comment/'.$mock->getID().'/')) ->setWorkflow(true) ->setFlexible(true) ->appendChild( id(new PhabricatorRemarkupControl()) ->setName('comment') ->setLabel(pht('Comment')) ->setUser($user)) ->appendChild( id(new AphrontFormSubmitControl()) ->setValue($action)); return array( $header, $form, ); } private function buildTransactionView( array $xactions, PhabricatorMarkupEngine $engine) { assert_instances_of($xactions, 'PholioTransaction'); $view = new PhabricatorTimelineView(); foreach ($xactions as $xaction) { $author = $this->getHandle($xaction->getAuthorPHID()); $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $xaction_visible = true; $title = null; $type = $xaction->getTransactionType(); switch ($type) { case PholioTransactionType::TYPE_NONE: $title = pht( '%s added a comment.', $author->renderLink()); break; case PholioTransactionType::TYPE_NAME: if ($old === null) { $xaction_visible = false; break; } $title = pht( '%s renamed this mock from "%s" to "%s".', $author->renderLink(), phutil_escape_html($old), phutil_escape_html($new)); break; case PholioTransactionType::TYPE_DESCRIPTION: if ($old === null) { $xaction_visible = false; break; } // TODO: Show diff, like Maniphest. $title = pht( '%s updated the description of this mock. '. 'The old description was: %s', $author->renderLink(), phutil_escape_html($old)); break; case PholioTransactionType::TYPE_VIEW_POLICY: if ($old === null) { $xaction_visible = false; break; } // TODO: Render human-readable. $title = pht( '%s changed the visibility of this mock from "%s" to "%s".', $author->renderLink(), phutil_escape_html($old), phutil_escape_html($new)); break; case PholioTransactionType::TYPE_SUBSCRIBERS: $rem = array_diff($old, $new); $add = array_diff($new, $old); $add_l = array(); foreach ($add as $phid) { $add_l[] = $this->getHandle($phid)->renderLink(); } $add_l = implode(', ', $add_l); $rem_l = array(); foreach ($rem as $phid) { $rem_l[] = $this->getHandle($phid)->renderLink(); } $rem_l = implode(', ', $rem_l); if ($add && $rem) { $title = pht( '%s edited subscriber(s), added %d: %s; removed %d: %s.', $author->renderLink(), $add_l, count($add), $rem_l, count($rem)); } else if ($add) { $title = pht( '%s added %d subscriber(s): %s.', $author->renderLink(), count($add), $add_l); } else if ($rem) { $title = pht( '%s removed %d subscribers: %s.', $author->renderLink(), count($rem), $rem_l); } break; default: throw new Exception("Unknown transaction type '{$type}'!"); } if (!$xaction_visible) { // Some transactions aren't useful to human viewers, like // the initial transactions which set the mock's name and description. continue; } $event = id(new PhabricatorTimelineEventView()) ->setUserHandle($author); $event->setTitle($title); if (strlen($xaction->getComment())) { $event->appendChild( $engine->getOutput( $xaction, PholioTransaction::MARKUP_FIELD_COMMENT)); } $view->addEvent($event); } return $view; } } diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index b988db2bf..582fb1e61 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -1,328 +1,312 @@ contentSource = $content_source; return $this; } public function getContentSource() { return $this->contentSource; } public function applyTransactions(PholioMock $mock, array $xactions) { assert_instances_of($xactions, 'PholioTransaction'); $actor = $this->requireActor(); if (!$this->contentSource) { throw new Exception( "Call setContentSource() before applyTransactions()!"); } $is_new = !$mock->getID(); $comments = array(); foreach ($xactions as $xaction) { if (strlen($xaction->getComment())) { $comments[] = $xaction->getComment(); } $type = $xaction->getTransactionType(); if ($type == PholioTransactionType::TYPE_DESCRIPTION) { $comments[] = $xaction->getNewValue(); } } $mentioned_phids = PhabricatorMarkupEngine::extractPHIDsFromMentions( $comments); $subscribe_phids = $mentioned_phids; // Attempt to subscribe the actor. $subscribe_phids[] = $actor->getPHID(); if ($subscribe_phids) { if ($mock->getID()) { $old_subs = PhabricatorSubscribersQuery::loadSubscribersForPHID( $mock->getPHID()); } else { $old_subs = array(); } $new_subs = array_merge($old_subs, $mentioned_phids); $xaction = id(new PholioTransaction()) ->setTransactionType(PholioTransactionType::TYPE_SUBSCRIBERS) ->setOldValue($old_subs) ->setNewValue($new_subs); array_unshift($xactions, $xaction); } foreach ($xactions as $xaction) { $xaction->setContentSource($this->contentSource); $xaction->setAuthorPHID($actor->getPHID()); } foreach ($xactions as $key => $xaction) { $has_effect = $this->applyTransaction($mock, $xaction); if (!$has_effect) { unset($xactions[$key]); } } if (!$xactions) { return; } $mock->openTransaction(); $mock->save(); foreach ($xactions as $xaction) { $xaction->setMockID($mock->getID()); $xaction->save(); } // Apply ID/PHID-dependent transactions. foreach ($xactions as $xaction) { $type = $xaction->getTransactionType(); switch ($type) { case PholioTransactionType::TYPE_SUBSCRIBERS: $subeditor = id(new PhabricatorSubscriptionsEditor()) ->setObject($mock) ->setActor($this->requireActor()) ->subscribeExplicit($xaction->getNewValue()) ->save(); break; } } $mock->saveTransaction(); $this->sendMail($mock, $xactions, $is_new, $mentioned_phids); PholioIndexer::indexMock($mock); return $this; } private function sendMail( PholioMock $mock, array $xactions, $is_new, array $mentioned_phids) { $subscribed_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( $mock->getPHID()); $email_to = array( $mock->getAuthorPHID(), $this->requireActor()->getPHID(), ); $email_cc = $subscribed_phids; $phids = array_merge($email_to, $email_cc); $handles = id(new PhabricatorObjectHandleData($phids)) ->setViewer($this->requireActor()) ->loadHandles(); $mock_id = $mock->getID(); $name = $mock->getName(); $original_name = $mock->getOriginalName(); $thread_id = 'pholio-mock-'.$mock->getPHID(); $mail_tags = $this->getMailTags($mock, $xactions); $body = new PhabricatorMetaMTAMailBody(); $body->addRawSection('lorem ipsum'); $mock_uri = PhabricatorEnv::getProductionURI('/M'.$mock->getID()); $body->addTextSection(pht('MOCK DETAIL'), $mock_uri); $reply_handler = $this->buildReplyHandler($mock); $template = id(new PhabricatorMetaMTAMail()) ->setSubject("M{$mock_id}: {$name}") ->setSubjectPrefix($this->getMailSubjectPrefix()) ->setVarySubjectPrefix('[edit/create?]') ->setFrom($this->requireActor()->getPHID()) ->addHeader('Thread-Topic', "M{$mock_id}: {$original_name}") ->setThreadID($thread_id, $is_new) ->setRelatedPHID($mock->getPHID()) ->setExcludeMailRecipientPHIDs($this->getExcludeMailRecipientPHIDs()) ->setIsBulk(true) ->setMailTags($mail_tags) ->setBody($body->render()); // TODO // ->setParentMessageID(...) $mails = $reply_handler->multiplexMail( $template, array_select_keys($handles, $email_to), array_select_keys($handles, $email_cc)); foreach ($mails as $mail) { $mail->saveAndSend(); } $template->addTos($email_to); $template->addCCs($email_cc); return $template; } private function getMailTags(PholioMock $mock, array $xactions) { assert_instances_of($xactions, 'PholioTransaction'); $tags = array(); return $tags; } public function buildReplyHandler(PholioMock $mock) { $handler_object = new PholioReplyHandler(); $handler_object->setMailReceiver($mock); return $handler_object; } private function getMailSubjectPrefix() { return PhabricatorEnv::getEnvConfig('metamta.pholio.subject-prefix'); } private function applyTransaction( PholioMock $mock, PholioTransaction $xaction) { $type = $xaction->getTransactionType(); $old = null; switch ($type) { case PholioTransactionType::TYPE_NONE: $old = null; break; case PholioTransactionType::TYPE_NAME: $old = $mock->getName(); break; case PholioTransactionType::TYPE_DESCRIPTION: $old = $mock->getDescription(); break; case PholioTransactionType::TYPE_VIEW_POLICY: $old = $mock->getViewPolicy(); break; case PholioTransactionType::TYPE_SUBSCRIBERS: $old = PhabricatorSubscribersQuery::loadSubscribersForPHID( $mock->getPHID()); break; default: throw new Exception("Unknown transaction type '{$type}'!"); } $xaction->setOldValue($old); if (!$this->transactionHasEffect($mock, $xaction)) { return false; } switch ($type) { case PholioTransactionType::TYPE_NONE: break; case PholioTransactionType::TYPE_NAME: $mock->setName($xaction->getNewValue()); if ($mock->getOriginalName() === null) { $mock->setOriginalName($xaction->getNewValue()); } break; case PholioTransactionType::TYPE_DESCRIPTION: $mock->setDescription($xaction->getNewValue()); break; case PholioTransactionType::TYPE_VIEW_POLICY: $mock->setViewPolicy($xaction->getNewValue()); break; case PholioTransactionType::TYPE_SUBSCRIBERS: // This applies later. break; default: throw new Exception("Unknown transaction type '{$type}'!"); } return true; } private function transactionHasEffect( PholioMock $mock, PholioTransaction $xaction) { $effect = false; $old = $xaction->getOldValue(); $new = $xaction->getNewValue(); $type = $xaction->getTransactionType(); switch ($type) { case PholioTransactionType::TYPE_NONE: case PholioTransactionType::TYPE_NAME: case PholioTransactionType::TYPE_DESCRIPTION: case PholioTransactionType::TYPE_VIEW_POLICY: $effect = ($old !== $new); break; case PholioTransactionType::TYPE_SUBSCRIBERS: $old = nonempty($old, array()); $old_map = array_fill_keys($old, true); $filtered = $old; foreach ($new as $phid) { if ($mock->getAuthorPHID() == $phid) { // The author may not be explicitly subscribed. continue; } if (isset($old_map[$phid])) { // This PHID was already subscribed. continue; } $filtered[] = $phid; } $old = array_keys($old_map); $new = array_values($filtered); $xaction->setOldValue($old); $xaction->setNewValue($new); $effect = ($old !== $new); break; default: throw new Exception("Unknown transaction type '{$type}'!"); } if (!$effect) { if (strlen($xaction->getComment())) { $xaction->setTransactionType(PholioTransactionType::TYPE_NONE); $effect = true; } } return $effect; } } diff --git a/src/applications/pholio/indexer/PholioIndexer.php b/src/applications/pholio/indexer/PholioIndexer.php index a95401ed4..2a87de40c 100644 --- a/src/applications/pholio/indexer/PholioIndexer.php +++ b/src/applications/pholio/indexer/PholioIndexer.php @@ -1,44 +1,28 @@ setPHID($mock->getPHID()); $doc->setDocumentType(phid_get_type($mock->getPHID())); $doc->setDocumentTitle($mock->getName()); $doc->setDocumentCreated($mock->getDateCreated()); $doc->setDocumentModified($mock->getDateModified()); $doc->addField( PhabricatorSearchField::FIELD_BODY, $mock->getDescription()); $doc->addRelationship( PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR, $mock->getAuthorPHID(), PhabricatorPHIDConstants::PHID_TYPE_USER, $mock->getDateCreated()); self::reindexAbstractDocument($doc); } } diff --git a/src/applications/pholio/mail/PholioReplyHandler.php b/src/applications/pholio/mail/PholioReplyHandler.php index e2ceb2c7d..47cd9f24b 100644 --- a/src/applications/pholio/mail/PholioReplyHandler.php +++ b/src/applications/pholio/mail/PholioReplyHandler.php @@ -1,59 +1,43 @@ getDefaultPrivateReplyHandlerEmailAddress($handle, 'M'); } public function getPublicReplyHandlerEmailAddress() { return $this->getDefaultPublicReplyHandlerEmailAddress('M'); } public function getReplyHandlerDomain() { return PhabricatorEnv::getEnvConfig( 'metamta.pholio.reply-handler-domain'); } public function getReplyHandlerInstructions() { if ($this->supportsReplies()) { // TODO: Implement. return null; return "Reply to comment."; } else { return null; } } protected function receiveEmail(PhabricatorMetaMTAReceivedMail $mail) { // TODO: Implement this. return null; } } diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php index 1964a1959..5aa6a5911 100644 --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -1,88 +1,72 @@ ids = $ids; return $this; } public function withPHIDs(array $phids) { $this->phids = $phids; return $this; } public function withAuthorPHIDs(array $author_phids) { $this->authorPHIDs = $author_phids; return $this; } public function loadPage() { $table = new PholioMock(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } private function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); $where[] = $this->buildPagingClause($conn_r); if ($this->ids) { $where[] = qsprintf( $conn_r, 'id IN (%Ld)', $this->ids); } if ($this->phids) { $where[] = qsprintf( $conn_r, 'phid IN (%Ls)', $this->phids); } if ($this->authorPHIDs) { $where[] = qsprintf( $conn_r, 'authorPHID in (%Ls)', $this->authorPHIDs); } return $this->formatWhereClause($where); } } diff --git a/src/applications/pholio/query/PholioTransactionQuery.php b/src/applications/pholio/query/PholioTransactionQuery.php index 376e49ddd..28761bbc8 100644 --- a/src/applications/pholio/query/PholioTransactionQuery.php +++ b/src/applications/pholio/query/PholioTransactionQuery.php @@ -1,64 +1,48 @@ mockIDs = $ids; return $this; } public function execute() { $table = new PholioTransaction(); $conn_r = $table->establishConnection('r'); $data = queryfx_all( $conn_r, 'SELECT * FROM %T x %Q %Q %Q', $table->getTableName(), $this->buildWhereClause($conn_r), $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $table->loadAllFromArray($data); } private function buildWhereClause(AphrontDatabaseConnection $conn_r) { $where = array(); if ($this->mockIDs) { $where[] = qsprintf( $conn_r, 'mockID IN (%Ld)', $this->mockIDs); } return $this->formatWhereClause($where); } private function buildOrderClause(AphrontDatabaseConnection $conn_r) { return 'ORDER BY id ASC'; } } diff --git a/src/applications/pholio/storage/PholioDAO.php b/src/applications/pholio/storage/PholioDAO.php index ba49bc747..e755e3317 100644 --- a/src/applications/pholio/storage/PholioDAO.php +++ b/src/applications/pholio/storage/PholioDAO.php @@ -1,28 +1,12 @@ getMarkupText($field)); return 'M:'.$hash; } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newMarkupEngine(array()); } public function getMarkupText($field) { return $this->getDescription(); } public function didMarkupText($field, $output, PhutilMarkupEngine $engine) { return $output; } public function shouldUseMarkupCache($field) { return (bool)$this->getID(); } } diff --git a/src/applications/pholio/storage/PholioMock.php b/src/applications/pholio/storage/PholioMock.php index 3e8274a5d..177771c95 100644 --- a/src/applications/pholio/storage/PholioMock.php +++ b/src/applications/pholio/storage/PholioMock.php @@ -1,113 +1,97 @@ true, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID('MOCK'); } public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); } return parent::save(); } /* -( PhabricatorSubscribableInterface Implementation )-------------------- */ public function isAutomaticallySubscribed($phid) { return ($this->authorPHID == $phid); } /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return PhabricatorPolicies::POLICY_NOONE; } } public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { return ($viewer->getPHID() == $this->getAuthorPHID()); } /* -( PhabricatorMarkupInterface )----------------------------------------- */ public function getMarkupFieldKey($field) { $hash = PhabricatorHash::digest($this->getMarkupText($field)); return 'M:'.$hash; } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newMarkupEngine(array()); } public function getMarkupText($field) { return $this->getDescription(); } public function didMarkupText($field, $output, PhutilMarkupEngine $engine) { return $output; } public function shouldUseMarkupCache($field) { return (bool)$this->getID(); } } diff --git a/src/applications/pholio/storage/PholioPixelComment.php b/src/applications/pholio/storage/PholioPixelComment.php index 3a2b23564..f7284333a 100644 --- a/src/applications/pholio/storage/PholioPixelComment.php +++ b/src/applications/pholio/storage/PholioPixelComment.php @@ -1,62 +1,46 @@ getID(); } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newMarkupEngine(array()); } public function getMarkupText($field) { return $this->getComment(); } public function didMarkupText($field, $output, PhutilMarkupEngine $engine) { return $output; } public function shouldUseMarkupCache($field) { return ($this->getID() && $this->getTransactionID()); } } diff --git a/src/applications/pholio/storage/PholioTransaction.php b/src/applications/pholio/storage/PholioTransaction.php index d7f3bbff4..60515ee4a 100644 --- a/src/applications/pholio/storage/PholioTransaction.php +++ b/src/applications/pholio/storage/PholioTransaction.php @@ -1,122 +1,106 @@ array( 'oldValue' => self::SERIALIZATION_JSON, 'newValue' => self::SERIALIZATION_JSON, 'metadata' => self::SERIALIZATION_JSON, ), ) + parent::getConfiguration(); } public function setContentSource(PhabricatorContentSource $content_source) { $this->contentSource = $content_source->serialize(); return $this; } public function getContentSource() { return PhabricatorContentSource::newFromSerialized($this->contentSource); } public function getRequiredHandlePHIDs() { switch ($this->getTransactionType()) { case PholioTransactionType::TYPE_SUBSCRIBERS: return array_merge( $this->getOldValue(), $this->getNewValue()); default: return array(); } } /* -( PhabricatorSubscribableInterface Implementation )-------------------- */ public function isAutomaticallySubscribed($phid) { return ($this->authorPHID == $phid); } /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return PhabricatorPolicies::POLICY_NOONE; } } public function hasAutomaticCapbility($capability, PhabricatorUser $viewer) { return ($viewer->getPHID() == $this->getAuthorPHID()); } /* -( PhabricatorMarkupInterface )----------------------------------------- */ public function getMarkupFieldKey($field) { return 'MX:'.$this->getID(); } public function newMarkupEngine($field) { return PhabricatorMarkupEngine::newMarkupEngine(array()); } public function getMarkupText($field) { return $this->getComment(); } public function didMarkupText($field, $output, PhutilMarkupEngine $engine) { return $output; } public function shouldUseMarkupCache($field) { return (bool)$this->getID(); } } diff --git a/src/applications/uiexample/examples/PhabricatorTimelineExample.php b/src/applications/uiexample/examples/PhabricatorTimelineExample.php index 8b7f3bb7c..5003532d9 100644 --- a/src/applications/uiexample/examples/PhabricatorTimelineExample.php +++ b/src/applications/uiexample/examples/PhabricatorTimelineExample.php @@ -1,103 +1,87 @@ PhabricatorTimelineView to comments and transactions.'; } public function renderExample() { $request = $this->getRequest(); $user = $request->getUser(); $handle = PhabricatorObjectHandleData::loadOneHandle( $user->getPHID(), $user); $events = array(); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('A major event.') ->appendChild('This is a major timeline event.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('A minor event.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->appendChild('A major event with no title.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Another minor event.'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Major Red Event') ->appendChild('This event is red!') ->addClass('phabricator-timeline-red'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Red Event') ->addClass('phabricator-timeline-red'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Not-Red Event'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Red Event') ->addClass('phabricator-timeline-red'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Minor Not-Red Event'); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Unstyled event') ->appendChild('This event disables standard title and content styling.') ->setDisableStandardTitleStyle(true) ->setDisableStandardContentStyle(true); $events[] = id(new PhabricatorTimelineEventView()) ->setUserHandle($handle) ->setTitle('Major Green Event') ->appendChild('This event is green!') ->addClass('phabricator-timeline-green'); $timeline = id(new PhabricatorTimelineView()); foreach ($events as $event) { $timeline->addEvent($event); } return $timeline; } } diff --git a/src/view/layout/PhabricatorTagView.php b/src/view/layout/PhabricatorTagView.php index f923c5b7a..165ed2b9f 100644 --- a/src/view/layout/PhabricatorTagView.php +++ b/src/view/layout/PhabricatorTagView.php @@ -1,183 +1,167 @@ type = $type; switch ($type) { case self::TYPE_OBJECT: $this->setBackgroundColor(self::COLOR_OBJECT); break; case self::TYPE_PERSON: $this->setBackgroundColor(self::COLOR_PERSON); break; } return $this; } public function setBarColor($bar_color) { $this->barColor = $bar_color; return $this; } public function setDotColor($dot_color) { $this->dotColor = $dot_color; return $this; } public function setBackgroundColor($background_color) { $this->backgroundColor = $background_color; return $this; } public function setPHID($phid) { $this->phid = $phid; return $this; } public function setName($name) { $this->name = $name; return $this; } public function setHref($href) { $this->href = $href; return $this; } public function setClosed($closed) { $this->closed = $closed; return $this; } public function render() { if (!$this->type) { throw new Exception("You must call setType() before render()!"); } require_celerity_resource('phabricator-tag-view-css'); $classes = array( 'phabricator-tag-view', 'phabricator-tag-type-'.$this->type, ); if ($this->closed) { $classes[] = 'phabricator-tag-state-closed'; } $color = null; if ($this->backgroundColor) { $color = 'phabricator-tag-color-'.$this->backgroundColor; } if ($this->dotColor) { $dotcolor = 'phabricator-tag-color-'.$this->dotColor; $dot = phutil_render_tag( 'span', array( 'class' => 'phabricator-tag-dot '.$dotcolor, ), ''); } else { $dot = null; } $content = phutil_render_tag( 'span', array( 'class' => 'phabricator-tag-core '.$color, ), $dot.phutil_escape_html($this->name)); if ($this->barColor) { $barcolor = 'phabricator-tag-color-'.$this->barColor; $bar = phutil_render_tag( 'span', array( 'class' => 'phabricator-tag-bar '.$barcolor, ), ''); $classes[] = 'phabricator-tag-view-has-bar'; } else { $bar = null; } return phutil_render_tag( $this->href ? 'a' : 'span', array( 'href' => $this->href, 'class' => implode(' ', $classes), ), $bar.$content); } public static function getTagTypes() { return array( self::TYPE_PERSON, self::TYPE_OBJECT, self::TYPE_STATE, ); } public static function getColors() { return array( self::COLOR_RED, self::COLOR_REDORANGE, self::COLOR_ORANGE, self::COLOR_YELLOW, self::COLOR_BLUE, self::COLOR_MAGENTA, self::COLOR_GREEN, self::COLOR_BLACK, self::COLOR_GREY, self::COLOR_WHITE, self::COLOR_OBJECT, self::COLOR_PERSON, ); } } diff --git a/src/view/layout/PhabricatorTimelineEventView.php b/src/view/layout/PhabricatorTimelineEventView.php index 6bc755c56..1a57d8a92 100644 --- a/src/view/layout/PhabricatorTimelineEventView.php +++ b/src/view/layout/PhabricatorTimelineEventView.php @@ -1,143 +1,127 @@ userHandle = $handle; return $this; } public function setTitle($title) { $this->title = $title; return $this; } public function addClass($class) { $this->classes[] = $class; return $this; } public function setDisableStandardTitleStyle($disable) { $this->disableStandardTitleStyle = $disable; return $this; } public function setDisableStandardContentStyle($disable) { $this->disableStandardContentStyle = $disable; return $this; } public function render() { $content = $this->renderChildren(); $title = $this->title; if (($title === null) && !strlen($content)) { $title = ''; } if ($title !== null) { $title_classes = array(); $title_classes[] = 'phabricator-timeline-title'; if (!$this->disableStandardTitleStyle) { $title_classes[] = 'phabricator-timeline-standard-title'; } $title = phutil_render_tag( 'div', array( 'class' => implode(' ', $title_classes), ), $title); } $wedge = phutil_render_tag( 'div', array( 'class' => 'phabricator-timeline-wedge phabricator-timeline-border', ), ''); $image_uri = $this->userHandle->getImageURI(); $image = phutil_render_tag( 'div', array( 'style' => 'background-image: url('.$image_uri.')', 'class' => 'phabricator-timeline-image', ), ''); $content_classes = array(); $content_classes[] = 'phabricator-timeline-content'; if (!$this->disableStandardContentStyle) { $content_classes[] = 'phabricator-timeline-standard-content'; } $classes = array(); $classes[] = 'phabricator-timeline-event-view'; $classes[] = 'phabricator-timeline-border'; if ($content) { $classes[] = 'phabricator-timeline-major-event'; $content = phutil_render_tag( 'div', array( 'class' => implode(' ', $content_classes), ), phutil_render_tag( 'div', array( 'class' => 'phabricator-timeline-inner-content', ), $title. phutil_render_tag( 'div', array( 'class' => 'phabricator-timeline-core-content', ), $content))); $content = $image.$wedge.$content; } else { $classes[] = 'phabricator-timeline-minor-event'; $content = phutil_render_tag( 'div', array( 'class' => implode(' ', $content_classes), ), $image.$wedge.$title); } return phutil_render_tag( 'div', array( 'class' => implode(' ', $this->classes), ), phutil_render_tag( 'div', array( 'class' => implode(' ', $classes), ), $content)); } } diff --git a/src/view/layout/PhabricatorTimelineView.php b/src/view/layout/PhabricatorTimelineView.php index 12116d3c9..91773e100 100644 --- a/src/view/layout/PhabricatorTimelineView.php +++ b/src/view/layout/PhabricatorTimelineView.php @@ -1,51 +1,35 @@ events[] = $event; return $this; } public function render() { require_celerity_resource('phabricator-timeline-view-css'); $events = array(); foreach ($this->events as $event) { $events[] = phutil_render_tag( 'div', array( 'class' => 'phabricator-timeline-event-view '. 'phabricator-timeline-spacer', ), ''); $events[] = $this->renderSingleView($event); } return phutil_render_tag( 'div', array( 'class' => 'phabricator-timeline-view', ), implode('', $events)); } }