1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 09:18:48 +02:00

Conpherence - add "room" search UI and create UI

Summary: Ref T7584. This hits all the major bullets there. Next step on T7584 is figuring out how it integrates into the full UI and column UI. That said, this is a bit buggy feeling right now since Conpherence as is assumes you are a participant all over the place and rooms make no such assumption. I'll probably this bit up next.

Test Plan:
viewed /conpherence/room/ and saw stuff. viewed the "participant" query as two different users and saw different correct result sets. made a room via the button and it worked. tried to view a room I wasn't a participant in and it failed horribly, which is something to fix in a future diff

created a thread via "send message" on a user profile and it worked

Reviewers: chad, epriestley

Reviewed By: epriestley

Subscribers: Korvin, epriestley

Maniphest Tasks: T7584

Differential Revision: https://secure.phabricator.com/D12113
This commit is contained in:
Bob Trahan 2015-03-24 13:04:33 -07:00
parent 86404a1a18
commit 014bb72050
10 changed files with 435 additions and 30 deletions

View file

@ -238,6 +238,7 @@ phutil_register_library_map(array(
'ConpherenceListController' => 'applications/conpherence/controller/ConpherenceListController.php',
'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php',
'ConpherenceNewController' => 'applications/conpherence/controller/ConpherenceNewController.php',
'ConpherenceNewRoomController' => 'applications/conpherence/controller/ConpherenceNewRoomController.php',
'ConpherenceNotificationPanelController' => 'applications/conpherence/controller/ConpherenceNotificationPanelController.php',
'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php',
'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php',
@ -247,6 +248,7 @@ phutil_register_library_map(array(
'ConpherenceQueryThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php',
'ConpherenceQueryTransactionConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php',
'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php',
'ConpherenceRoomListController' => 'applications/conpherence/controller/ConpherenceRoomListController.php',
'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php',
'ConpherenceSettings' => 'applications/conpherence/constants/ConpherenceSettings.php',
'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
@ -254,6 +256,7 @@ phutil_register_library_map(array(
'ConpherenceThreadListView' => 'applications/conpherence/view/ConpherenceThreadListView.php',
'ConpherenceThreadMailReceiver' => 'applications/conpherence/mail/ConpherenceThreadMailReceiver.php',
'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php',
'ConpherenceThreadSearchEngine' => 'applications/conpherence/query/ConpherenceThreadSearchEngine.php',
'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php',
'ConpherenceTransactionComment' => 'applications/conpherence/storage/ConpherenceTransactionComment.php',
'ConpherenceTransactionQuery' => 'applications/conpherence/query/ConpherenceTransactionQuery.php',
@ -3399,6 +3402,7 @@ phutil_register_library_map(array(
'ConpherenceListController' => 'ConpherenceController',
'ConpherenceMenuItemView' => 'AphrontTagView',
'ConpherenceNewController' => 'ConpherenceController',
'ConpherenceNewRoomController' => 'ConpherenceController',
'ConpherenceNotificationPanelController' => 'ConpherenceController',
'ConpherenceParticipant' => 'ConpherenceDAO',
'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery',
@ -3408,6 +3412,7 @@ phutil_register_library_map(array(
'ConpherenceQueryThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
'ConpherenceQueryTransactionConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler',
'ConpherenceRoomListController' => 'ConpherenceController',
'ConpherenceSchemaSpec' => 'PhabricatorConfigSchemaSpec',
'ConpherenceSettings' => 'ConpherenceConstants',
'ConpherenceThread' => array(
@ -3418,6 +3423,7 @@ phutil_register_library_map(array(
'ConpherenceThreadListView' => 'AphrontView',
'ConpherenceThreadMailReceiver' => 'PhabricatorObjectMailReceiver',
'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'ConpherenceThreadSearchEngine' => 'PhabricatorApplicationSearchEngine',
'ConpherenceTransaction' => 'PhabricatorApplicationTransaction',
'ConpherenceTransactionComment' => 'PhabricatorApplicationTransactionComment',
'ConpherenceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',

View file

@ -36,6 +36,10 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication {
'(?P<id>[1-9]\d*)/' => 'ConpherenceViewController',
'columnview/' => 'ConpherenceColumnViewController',
'new/' => 'ConpherenceNewController',
'room/' => array(
'(?:query/(?P<queryKey>[^/]+)/)?' => 'ConpherenceRoomListController',
'new/' => 'ConpherenceNewRoomController',
),
'panel/' => 'ConpherenceNotificationPanelController',
'widget/(?P<id>[1-9]\d*)/' => 'ConpherenceWidgetController',
'update/(?P<id>[1-9]\d*)/' => 'ConpherenceUpdateController',

View file

@ -39,24 +39,38 @@ abstract class ConpherenceController extends PhabricatorController {
}
protected function buildApplicationCrumbs() {
return $this->buildConpherenceApplicationCrumbs();
}
protected function buildConpherenceApplicationCrumbs($is_rooms = false) {
$crumbs = parent::buildApplicationCrumbs();
$crumbs->setBorder(true);
$crumbs
->addAction(
id(new PHUIListItemView())
->setName(pht('New Message'))
->setHref($this->getApplicationURI('new/'))
->setIcon('fa-plus-square')
->setWorkflow(true))
->addAction(
id(new PHUIListItemView())
->setName(pht('Thread'))
->setHref('#')
->setIcon('fa-bars')
->setStyle('display: none;')
->addClass('device-widgets-selector')
->addSigil('device-widgets-selector'));
if ($is_rooms) {
$crumbs
->addAction(
id(new PHUIListItemView())
->setName(pht('New Room'))
->setHref($this->getApplicationURI('room/new/'))
->setIcon('fa-plus-square')
->setWorkflow(true));
} else {
$crumbs
->addAction(
id(new PHUIListItemView())
->setName(pht('New Message'))
->setHref($this->getApplicationURI('new/'))
->setIcon('fa-plus-square')
->setWorkflow(true))
->addAction(
id(new PHUIListItemView())
->setName(pht('Thread'))
->setHref('#')
->setIcon('fa-bars')
->setStyle('display: none;')
->addClass('device-widgets-selector')
->addSigil('device-widgets-selector'));
}
return $crumbs;
}

View file

@ -0,0 +1,102 @@
<?php
final class ConpherenceNewRoomController extends ConpherenceController {
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$title = pht('New Room');
$e_title = true;
$validation_exception = null;
$conpherence = ConpherenceThread::initializeNewRoom($user);
if ($request->isFormPost()) {
$xactions = array();
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_PARTICIPANTS)
->setNewValue(array('+' => array($user->getPHID())));
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
->setNewValue($request->getStr('title'));
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($request->getStr('viewPolicy'));
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($request->getStr('editPolicy'));
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY)
->setNewValue($request->getStr('joinPolicy'));
try {
id(new ConpherenceEditor())
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setActor($user)
->applyTransactions($conpherence, $xactions);
$uri = $this->getApplicationURI($conpherence->getID());
return id(new AphrontRedirectResponse())
->setURI($uri);
} catch (PhabricatorApplicationTransactionValidationException $ex) {
$validation_exception = $ex;
$e_title = $ex->getShortMessage(ConpherenceTransactionType::TYPE_TITLE);
$conpherence->setViewPolicy($request->getStr('viewPolicy'));
$conpherence->setEditPolicy($request->getStr('editPolicy'));
$conpherence->setJoinPolicy($request->getStr('joinPolicy'));
}
}
$policies = id(new PhabricatorPolicyQuery())
->setViewer($user)
->setObject($conpherence)
->execute();
$submit_uri = $this->getApplicationURI('room/new/');
$cancel_uri = $this->getApplicationURI('room/');
$dialog = $this->newDialog()
->setWidth(AphrontDialogView::WIDTH_FORM)
->setValidationException($validation_exception)
->setUser($user)
->setTitle($title)
->addCancelButton($cancel_uri)
->addSubmitButton(pht('Create Room'));
$form = id(new PHUIFormLayoutView())
->setUser($user)
->setFullWidth(true)
->appendChild(
id(new AphrontFormTextControl())
->setError($e_title)
->setLabel(pht('Title'))
->setName('title')
->setValue($request->getStr('title')))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('viewPolicy')
->setPolicyObject($conpherence)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($conpherence)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('joinPolicy')
->setPolicyObject($conpherence)
->setCapability(PhabricatorPolicyCapability::CAN_JOIN)
->setPolicies($policies));
$dialog->appendChild($form);
return id(new AphrontDialogResponse())->setDialog($dialog);
}
}

View file

@ -0,0 +1,46 @@
<?php
final class ConpherenceRoomListController extends ConpherenceController {
public function handleRequest(AphrontRequest $request) {
$user = $request->getUser();
$controller = id(new PhabricatorApplicationSearchController())
->setQueryKey($request->getURIData('queryKey'))
->setSearchEngine(
id(new ConpherenceThreadSearchEngine())
->setIsRooms(true))
->setNavigation($this->buildRoomsSideNavView());
return $this->delegateToController($controller);
}
protected function buildApplicationCrumbs() {
return $this->buildConpherenceApplicationCrumbs($is_rooms = true);
}
public function buildApplicationMenu() {
return $this->buildRoomsSideNavView(true)->getMenu();
}
private function buildRoomsSideNavView($for_app = false) {
$user = $this->getRequest()->getUser();
$nav = new AphrontSideNavFilterView();
$nav->setBaseURI(new PhutilURI($this->getApplicationURI()));
if ($for_app) {
$nav->addFilter('room/new/', pht('Create Room'));
}
id(new ConpherenceThreadSearchEngine())
->setViewer($user)
->addNavigationItems($nav->getMenu());
$nav->selectFilter(null);
return $nav;
}
}

View file

@ -61,6 +61,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
->setNewValue($title);
}
$xactions[] = id(new ConpherenceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
->attachComment(
@ -73,7 +74,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
->setContinueOnNoEffect(true)
->setActor($creator)
->applyTransactions($conpherence, $xactions);
}
return array($errors, $conpherence);
@ -122,6 +122,9 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
$types[] = ConpherenceTransactionType::TYPE_TITLE;
$types[] = ConpherenceTransactionType::TYPE_PARTICIPANTS;
$types[] = ConpherenceTransactionType::TYPE_FILES;
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
$types[] = PhabricatorTransactions::TYPE_JOIN_POLICY;
return $types;
}
@ -483,6 +486,30 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
$errors = parent::validateTransaction($object, $type, $xactions);
switch ($type) {
case ConpherenceTransactionType::TYPE_TITLE:
if (!$object->getIsRoom() && $this->getIsNewObject()) {
continue;
}
$missing = $this->validateIsEmptyTextField(
$object->getTitle(),
$xactions);
if ($missing) {
if ($object->getIsRoom()) {
$detail = pht('Room title is required.');
} else {
$detail = pht('Thread title can not be blank.');
}
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
$detail,
last($xactions));
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
break;
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
foreach ($xactions as $xaction) {
$phids = $this->getPHIDTransactionNewValue(

View file

@ -7,6 +7,7 @@ final class ConpherenceThreadQuery
private $phids;
private $ids;
private $participantPHIDs;
private $isRoom;
private $needWidgetData;
private $needTransactions;
@ -46,6 +47,11 @@ final class ConpherenceThreadQuery
return $this;
}
public function withParticipantPHIDs(array $phids) {
$this->participantPHIDs = $phids;
return $this;
}
public function withIsRoom($bool) {
$this->isRoom = $bool;
return $this;
@ -76,8 +82,9 @@ final class ConpherenceThreadQuery
$data = queryfx_all(
$conn_r,
'SELECT conpherence_thread.* FROM %T conpherence_thread %Q %Q %Q',
'SELECT conpherence_thread.* FROM %T conpherence_thread %Q %Q %Q %Q',
$table->getTableName(),
$this->buildJoinClause($conn_r),
$this->buildWhereClause($conn_r),
$this->buildOrderClause($conn_r),
$this->buildLimitClause($conn_r));
@ -106,6 +113,28 @@ final class ConpherenceThreadQuery
return $conpherences;
}
private function buildGroupClause($conn_r) {
if ($this->participantPHIDs !== null) {
return 'GROUP BY conpherence_thread.id';
} else {
return $this->buildApplicationSearchGroupClause($conn_r);
}
}
private function buildJoinClause($conn_r) {
$joins = array();
if ($this->participantPHIDs !== null) {
$joins[] = qsprintf(
$conn_r,
'JOIN %T p ON p.conpherencePHID = conpherence_thread.phid',
id(new ConpherenceParticipant())->getTableName());
}
$joins[] = $this->buildApplicationSearchJoinClause($conn_r);
return implode(' ', $joins);
}
protected function buildWhereClause($conn_r) {
$where = array();
@ -114,21 +143,28 @@ final class ConpherenceThreadQuery
if ($this->ids !== null) {
$where[] = qsprintf(
$conn_r,
'id IN (%Ld)',
'conpherence_thread.id IN (%Ld)',
$this->ids);
}
if ($this->phids !== null) {
$where[] = qsprintf(
$conn_r,
'phid IN (%Ls)',
'conpherence_thread.phid IN (%Ls)',
$this->phids);
}
if ($this->participantPHIDs !== null) {
$where[] = qsprintf(
$conn_r,
'p.participantPHID IN (%Ls)',
$this->participantPHIDs);
}
if ($this->isRoom !== null) {
$where[] = qsprintf(
$conn_r,
'isRoom = %d',
'conpherence_thread.isRoom = %d',
(int)$this->isRoom);
}

View file

@ -0,0 +1,156 @@
<?php
final class ConpherenceThreadSearchEngine
extends PhabricatorApplicationSearchEngine {
// For now, we only search for rooms, but write this code so its easy to
// change that decision later
private $isRooms = true;
public function setIsRooms($bool) {
$this->isRooms = $bool;
return $this;
}
public function getResultTypeDescription() {
if ($this->isRooms) {
$type = pht('Rooms');
} else {
$type = pht('Threads');
}
return $type;
}
public function getApplicationClassName() {
return 'PhabricatorConpherenceApplication';
}
public function buildSavedQueryFromRequest(AphrontRequest $request) {
$saved = new PhabricatorSavedQuery();
$saved->setParameter(
'participantPHIDs',
$this->readUsersFromRequest($request, 'participants'));
return $saved;
}
public function buildQueryFromSavedQuery(PhabricatorSavedQuery $saved) {
$query = id(new ConpherenceThreadQuery())
->withIsRoom($this->isRooms)
->needParticipantCache(true);
$participant_phids = $saved->getParameter('participantPHIDs', array());
if ($participant_phids && is_array($participant_phids)) {
$query->withParticipantPHIDs($participant_phids);
}
return $query;
}
public function buildSearchForm(
AphrontFormView $form,
PhabricatorSavedQuery $saved) {
$phids = $saved->getParameter('participantPHIDs', array());
$participant_handles = id(new PhabricatorHandleQuery())
->setViewer($this->requireViewer())
->withPHIDs($phids)
->execute();
$form
->appendChild(
id(new AphrontFormTokenizerControl())
->setDatasource(new PhabricatorPeopleDatasource())
->setName('participants')
->setLabel(pht('Participants'))
->setValue($participant_handles));
}
protected function getURI($path) {
if ($this->isRooms) {
return '/conpherence/room/'.$path;
} else {
// TODO - will need a path if / when "thread" search happens
}
}
protected function getBuiltinQueryNames() {
$names = array();
if ($this->isRooms) {
$names = array(
'all' => pht('All Rooms'),
);
if ($this->requireViewer()->isLoggedIn()) {
$names['participant'] = pht('Participated');
}
}
return $names;
}
public function buildSavedQueryFromBuiltin($query_key) {
$query = $this->newSavedQuery();
$query->setQueryKey($query_key);
switch ($query_key) {
case 'all':
return $query;
case 'participant':
return $query->setParameter(
'participantPHIDs',
array($this->requireViewer()->getPHID()));
}
return parent::buildSavedQueryFromBuiltin($query_key);
}
protected function getRequiredHandlePHIDsForResultList(
array $conpherences,
PhabricatorSavedQuery $query) {
$recent = mpull($conpherences, 'getRecentParticipantPHIDs');
return array_unique(array_mergev($recent));
}
protected function renderResultList(
array $conpherences,
PhabricatorSavedQuery $query,
array $handles) {
assert_instances_of($conpherences, 'ConpherenceThread');
$viewer = $this->requireViewer();
$list = new PHUIObjectItemListView();
$list->setUser($viewer);
foreach ($conpherences as $conpherence) {
$created = phabricator_date($conpherence->getDateCreated(), $viewer);
$data = $conpherence->getDisplayData($viewer);
$title = $data['title'];
$item = id(new PHUIObjectItemView())
->setObjectName($conpherence->getMonogram())
->setHeader($title)
->setHref('/conpherence/'.$conpherence->getID().'/')
->setObject($conpherence)
->addIcon('none', $created)
->addIcon(
'none',
pht('Messages: %d', $conpherence->getMessageCount()))
->addAttribute(
array(
id(new PHUIIconView())->setIconFont('fa-envelope-o', 'green'),
' ',
pht(
'Last updated %s',
phabricator_datetime($conpherence->getDateModified(), $viewer)),
));
$list->addItem($item);
}
return $list;
}
}

View file

@ -31,6 +31,8 @@ final class ConpherenceThread extends ConpherenceDAO
}
public static function initializeNewRoom(PhabricatorUser $creator) {
$participant_phids = array($creator->getPHID());
return id(new ConpherenceThread())
->setIsRoom(1)
->setMessageCount(0)
@ -39,7 +41,8 @@ final class ConpherenceThread extends ConpherenceDAO
->attachFilePHIDs(array())
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
->setEditPolicy($creator->getPHID())
->setJoinPolicy(PhabricatorPolicies::POLICY_USER);
->setJoinPolicy(PhabricatorPolicies::POLICY_USER)
->setRecentParticipantPHIDs($participant_phids);
}
protected function getConfiguration() {
@ -79,6 +82,10 @@ final class ConpherenceThread extends ConpherenceDAO
return parent::save();
}
public function getMonogram() {
return 'Z'.$this->getID();
}
public function attachParticipants(array $participants) {
assert_instances_of($participants, 'ConpherenceParticipant');
$this->participants = $participants;
@ -91,6 +98,11 @@ final class ConpherenceThread extends ConpherenceDAO
$participants = $this->getParticipants();
return $participants[$phid];
}
public function getParticipantIfExists($phid) {
$participants = $this->getParticipants();
return idx($participants, $phid);
}
public function getParticipantPHIDs() {
$participants = $this->getParticipants();
return array_keys($participants);
@ -191,10 +203,13 @@ final class ConpherenceThread extends ConpherenceDAO
$final = $count == 3;
}
$participants = $this->getParticipants();
$user_participation = $participants[$user->getPHID()];
$unread_count = $this->getMessageCount() -
$user_participation->getSeenMessageCount();
$user_participation = $this->getParticipantIfExists($user->getPHID());
if ($user_participation) {
$user_seen_count = $user_participation->getSeenMessageCount();
} else {
$user_seen_count = 0;
}
$unread_count = $this->getMessageCount() - $user_seen_count;
return array(
'title' => $title,

View file

@ -89,15 +89,14 @@ final class ConpherenceTransactionView extends AphrontView {
$content_class = null;
$content = null;
switch ($transaction->getTransactionType()) {
case ConpherenceTransactionType::TYPE_TITLE:
$content = $transaction->getTitle();
$transaction_view->addClass('conpherence-edited');
$transaction_view->addClass('grouped');
break;
case ConpherenceTransactionType::TYPE_FILES:
$content = $transaction->getTitle();
break;
case ConpherenceTransactionType::TYPE_TITLE:
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
$content = $transaction->getTitle();
$transaction_view->addClass('conpherence-edited');
$transaction_view->addClass('grouped');