diff --git a/src/applications/pholio/controller/PholioMockListController.php b/src/applications/pholio/controller/PholioMockListController.php index 943eab946a..5c456ec53d 100644 --- a/src/applications/pholio/controller/PholioMockListController.php +++ b/src/applications/pholio/controller/PholioMockListController.php @@ -1,62 +1,64 @@ view = idx($data, 'view'); } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $query = id(new PholioMockQuery()) - ->setViewer($user); + ->setViewer($user) + ->needCoverFiles(true); $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())); + ->setURI('/M'.$mock->getID()) + ->setImageURI($mock->getCoverFile()->getThumb160x120URI())); } $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 b15149ac34..0bf0022fe1 100644 --- a/src/applications/pholio/controller/PholioMockViewController.php +++ b/src/applications/pholio/controller/PholioMockViewController.php @@ -1,192 +1,194 @@ id = $data['id']; } public function processRequest() { $request = $this->getRequest(); $user = $request->getUser(); $mock = id(new PholioMockQuery()) ->setViewer($user) ->withIDs(array($this->id)) + ->needImages(true) + ->needCoverFiles(true) ->executeOne(); if (!$mock) { return new Aphront404Response(); } $xactions = id(new PholioTransactionQuery()) ->setViewer($user) ->withObjectPHIDs(array($mock->getPHID())) ->execute(); $subscribers = PhabricatorSubscribersQuery::loadSubscribersForPHID( $mock->getPHID()); $phids = array(); $phids[] = $mock->getAuthorPHID(); 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) { if ($xaction->getComment()) { $engine->addObject( $xaction->getComment(), PhabricatorApplicationTransactionComment::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); require_celerity_resource('pholio-css'); $output = new PholioMockImagesView(); $output->setMock($mock); $carousel = $output->render(); $xaction_view = id(new PhabricatorApplicationTransactionView()) ->setUser($this->getRequest()->getUser()) ->setTransactions($xactions) ->setMarkupEngine($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(); $draft = PhabricatorDraft::newFromUserAndKey($user, $mock->getPHID()); $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); $title = $is_serious ? pht('Add Comment') : pht('History Beckons'); $header = id(new PhabricatorHeaderView()) ->setHeader($title); $button_name = $is_serious ? pht('Add Comment') : pht('Answer The Call'); $form = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($user) ->setDraft($draft) ->setSubmitButtonName($button_name) ->setAction($this->getApplicationURI('/comment/'.$mock->getID().'/')); return array( $header, $form, ); } } diff --git a/src/applications/pholio/query/PholioMockQuery.php b/src/applications/pholio/query/PholioMockQuery.php index 5aa6a5911b..bbd56e9f5f 100644 --- a/src/applications/pholio/query/PholioMockQuery.php +++ b/src/applications/pholio/query/PholioMockQuery.php @@ -1,72 +1,130 @@ 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); + $mocks = $table->loadAllFromArray($data); + + if ($mocks && $this->needImages) { + $this->loadImages($mocks); + } + if ($mocks && $this->needCoverFiles) { + $this->loadCoverFiles($mocks); + } + + + return $mocks; } 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); } + public function needCoverFiles($need_cover_files) { + $this->needCoverFiles = $need_cover_files; + return $this; + } + + public function needImages($need_images) { + $this->needImages = $need_images; + return $this; + } + + public function loadImages(array $mocks) { + assert_instances_of($mocks, 'PholioMock'); + + $mock_ids = mpull($mocks, 'getID'); + $all_images = id(new PholioImage())->loadAllWhere( + 'mockID IN (%Ld)', + $mock_ids); + + $file_phids = mpull($all_images, 'getFilePHID'); + $all_files = mpull(id(new PhabricatorFile())->loadAllWhere( + 'phid IN (%Ls)', + $file_phids), null, 'getPHID'); + + foreach ($all_images as $image) { + $image->attachFile($all_files[$image->getFilePHID()]); + } + + $image_groups = mgroup($all_images, 'getMockID'); + + foreach ($mocks as $mock) { + $mock->attachImages($image_groups[$mock->getID()]); + } + } + + public function loadCoverFiles(array $mocks) { + assert_instances_of($mocks, 'PholioMock'); + $cover_file_phids = mpull($mocks, 'getCoverPHID'); + $cover_files = mpull(id(new PhabricatorFile())->loadAllWhere( + 'phid IN (%Ls)', + $cover_file_phids), null, 'getPHID'); + + foreach ($mocks as $mock) { + $mock->attachCoverFile($cover_files[$mock->getCoverPHID()]); + } + } } diff --git a/src/applications/pholio/storage/PholioImage.php b/src/applications/pholio/storage/PholioImage.php index 5525ff45a5..62e1862be1 100644 --- a/src/applications/pholio/storage/PholioImage.php +++ b/src/applications/pholio/storage/PholioImage.php @@ -1,42 +1,55 @@ 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(); } + public function attachFile(PhabricatorFile $file) { + $this->file = $file; + return $this; + } + + public function getFile() { + if ($this->file === null) { + throw new Exception("Call attachFile() before getFile()!"); + } + return $this->file; + } + } diff --git a/src/applications/pholio/storage/PholioMock.php b/src/applications/pholio/storage/PholioMock.php index 177771c95e..986c9685da 100644 --- a/src/applications/pholio/storage/PholioMock.php +++ b/src/applications/pholio/storage/PholioMock.php @@ -1,97 +1,125 @@ true, ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID('MOCK'); } public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); } return parent::save(); } + public function attachImages(array $images) { + assert_instances_of($images, 'PholioImage'); + $this->images = $images; + return $this; + } + + public function getImages() { + if ($this->images === null) { + throw new Exception("Call attachImages() before getImages()!"); + } + return $this->images; + } + + public function attachCoverFile(PhabricatorFile $file) { + $this->coverFile = $file; + return $this; + } + + public function getCoverFile() { + if ($this->coverFile === null) { + throw new Exception("Call attachCoverFile() before getCoverFile()!"); + } + return $this->coverFile; + } + /* -( 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/view/PholioMockImagesView.php b/src/applications/pholio/view/PholioMockImagesView.php index 47383f5918..103b751a6b 100644 --- a/src/applications/pholio/view/PholioMockImagesView.php +++ b/src/applications/pholio/view/PholioMockImagesView.php @@ -1,40 +1,34 @@ mock = $mock; } public function render() { if (!$this->mock) { throw new Exception("Call setMock() before render()!"); } - $image = id(new PholioImage())->loadOneWhere( - "mockid=%d", - $this->mock->getID()); - - $file = id(new PhabricatorFile())->loadOneWhere( - "phid=%s", - $image->getFilePHID()); + $file = head($this->mock->getImages())->getFile(); $image_tag = phutil_render_tag( 'img', array( 'src' => $file->getBestURI(), 'class' => 'pholio-mock-image', ), ''); return phutil_render_tag( 'div', array( 'class' => 'pholio-mock-image-container', ), $image_tag); } }