diff --git a/src/applications/conpherence/controller/ConpherenceController.php b/src/applications/conpherence/controller/ConpherenceController.php index e6690a55d1..09708b7148 100644 --- a/src/applications/conpherence/controller/ConpherenceController.php +++ b/src/applications/conpherence/controller/ConpherenceController.php @@ -1,225 +1,221 @@ conpherence = $conpherence; return $this; } public function getConpherence() { return $this->conpherence; } public function buildApplicationMenu() { $nav = new PHUIListView(); $conpherence = $this->conpherence; // Local Links if ($conpherence) { $nav->addMenuItem( id(new PHUIListItemView()) ->setName(pht('Joined Rooms')) ->setType(PHUIListItemView::TYPE_LINK) ->setHref($this->getApplicationURI())); $nav->addMenuItem( id(new PHUIListItemView()) ->setName(pht('Edit Room')) ->setType(PHUIListItemView::TYPE_LINK) ->setHref( $this->getApplicationURI('update/'.$conpherence->getID()).'/') ->setWorkflow(true)); $nav->addMenuItem( id(new PHUIListItemView()) ->setName(pht('Add Participants')) ->setType(PHUIListItemView::TYPE_LINK) ->setHref('#') ->addSigil('conpherence-widget-adder') ->setMetadata(array('widget' => 'widgets-people'))); } // Global Links $nav->newLabel(pht('Conpherence')); $nav->newLink( pht('New Room'), $this->getApplicationURI('new/')); $nav->newLink( pht('Search Rooms'), $this->getApplicationURI('search/')); return $nav; } protected function buildHeaderPaneContent( ConpherenceThread $conpherence, array $policy_objects) { assert_instances_of($policy_objects, 'PhabricatorPolicy'); $viewer = $this->getViewer(); $header = null; if ($conpherence->getID()) { $data = $conpherence->getDisplayData($this->getViewer()); $header = id(new PHUIHeaderView()) ->setHeader($data['title']) ->setSubheader($data['topic']) ->setImage($data['image']); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, $conpherence, PhabricatorPolicyCapability::CAN_EDIT); if ($can_edit) { $header->setImageURL( $this->getApplicationURI('picture/'.$conpherence->getID().'/')); } $participating = $conpherence->getParticipantIfExists($viewer->getPHID()); - $can_join = PhabricatorPolicyFilter::hasCapability( - $viewer, - $conpherence, - PhabricatorPolicyCapability::CAN_JOIN); $header->addActionItem( id(new PHUIIconCircleView()) ->setHref( $this->getApplicationURI('update/'.$conpherence->getID()).'/') ->setIcon('fa-pencil') ->addClass('hide-on-device') ->setColor('violet') ->setWorkflow(true)); $header->addActionItem( id(new PHUIIconCircleView()) ->setHref( $this->getApplicationURI('update/'.$conpherence->getID()).'/'. '?action='.ConpherenceUpdateActions::NOTIFICATIONS) ->setIcon('fa-gear') ->addClass('hide-on-device') ->setColor('pink') ->setWorkflow(true)); $widget_key = PhabricatorConpherenceWidgetVisibleSetting::SETTINGKEY; $widget_view = (bool)$viewer->getUserSetting($widget_key, false); Javelin::initBehavior( 'toggle-widget', array( 'show' => (int)$widget_view, 'settingsURI' => '/settings/adjust/?key='.$widget_key, )); $header->addActionItem( id(new PHUIIconCircleView()) ->addSigil('conpherence-widget-toggle') ->setIcon('fa-group') ->setHref('#') ->addClass('conpherence-participant-toggle')); Javelin::initBehavior('conpherence-search'); $header->addActionItem( id(new PHUIIconCircleView()) ->addSigil('conpherence-search-toggle') ->setIcon('fa-search') ->setHref('#') ->setColor('green') ->addClass('conpherence-search-toggle')); - if ($can_join && !$participating) { + if (!$participating) { $action = ConpherenceUpdateActions::JOIN_ROOM; $uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); $button = phutil_tag( 'button', array( 'type' => 'SUBMIT', 'class' => 'button green mlr', ), pht('Join Room')); $hidden = phutil_tag( 'input', array( 'type' => 'hidden', 'name' => 'action', 'value' => ConpherenceUpdateActions::JOIN_ROOM, )); $form = phabricator_form( $viewer, array( 'method' => 'POST', 'action' => (string)$uri, ), array( $hidden, $button, )); $header->addActionItem($form); } } return $header; } public function buildSearchForm() { $viewer = $this->getViewer(); $conpherence = $this->conpherence; $name = $conpherence->getTitle(); $bar = javelin_tag( 'input', array( 'type' => 'text', 'id' => 'conpherence-search-input', 'name' => 'fulltext', 'class' => 'conpherence-search-input', 'sigil' => 'conpherence-search-input', 'placeholder' => pht('Search %s...', $name), )); $id = $conpherence->getID(); $form = phabricator_form( $viewer, array( 'method' => 'POST', 'action' => '/conpherence/threadsearch/'.$id.'/', 'sigil' => 'conpherence-search-form', 'class' => 'conpherence-search-form', 'id' => 'conpherence-search-form', ), array( $bar, )); $form_view = phutil_tag( 'div', array( 'class' => 'conpherence-search-form-view', ), $form); $results = phutil_tag( 'div', array( 'id' => 'conpherence-search-results', 'class' => 'conpherence-search-results', )); $view = phutil_tag( 'div', array( 'class' => 'conpherence-search-window', ), array( $form_view, $results, )); return $view; } } diff --git a/src/applications/conpherence/controller/ConpherenceNewRoomController.php b/src/applications/conpherence/controller/ConpherenceNewRoomController.php index 1f27a6807e..d70395dbc9 100644 --- a/src/applications/conpherence/controller/ConpherenceNewRoomController.php +++ b/src/applications/conpherence/controller/ConpherenceNewRoomController.php @@ -1,126 +1,116 @@ getUser(); $title = pht('New Room'); $e_title = true; $validation_exception = null; $conpherence = ConpherenceThread::initializeNewRoom($user); $participants = array(); if ($request->isFormPost()) { $editor = new ConpherenceEditor(); $xactions = array(); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceThreadTitleTransaction::TRANSACTIONTYPE) ->setNewValue($request->getStr('title')); $participants = $request->getArr('participants'); $participants[] = $user->getPHID(); $participants = array_unique($participants); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) ->setNewValue(array('+' => $participants)); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceThreadTopicTransaction::TRANSACTIONTYPE) ->setNewValue($request->getStr('topic')); $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 { $editor ->setContentSourceFromRequest($request) ->setContinueOnNoEffect(true) ->setActor($user) ->applyTransactions($conpherence, $xactions); return id(new AphrontRedirectResponse()) ->setURI('/'.$conpherence->getMonogram()); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; $e_title = $ex->getShortMessage( ConpherenceThreadTitleTransaction::TRANSACTIONTYPE); $conpherence->setViewPolicy($request->getStr('viewPolicy')); $conpherence->setEditPolicy($request->getStr('editPolicy')); - $conpherence->setJoinPolicy($request->getStr('joinPolicy')); } } else { if ($request->getStr('participant')) { $participants[] = $request->getStr('participant'); } } $policies = id(new PhabricatorPolicyQuery()) ->setViewer($user) ->setObject($conpherence) ->execute(); $submit_uri = $this->getApplicationURI('new/'); $cancel_uri = $this->getApplicationURI('search/'); $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) ->appendChild( id(new AphrontFormTextControl()) ->setError($e_title) ->setLabel(pht('Name')) ->setName('title') ->setValue($request->getStr('title'))) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Topic')) ->setName('topic') ->setValue($request->getStr('topic'))) ->appendChild( id(new AphrontFormTokenizerControl()) ->setName('participants') ->setUser($user) ->setDatasource(new PhabricatorPeopleDatasource()) ->setValue($participants) ->setLabel(pht('Other Participants'))) ->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); } } diff --git a/src/applications/conpherence/controller/ConpherenceUpdateController.php b/src/applications/conpherence/controller/ConpherenceUpdateController.php index 44e6c3cf4b..b8ebe5a826 100644 --- a/src/applications/conpherence/controller/ConpherenceUpdateController.php +++ b/src/applications/conpherence/controller/ConpherenceUpdateController.php @@ -1,594 +1,575 @@ getUser(); $conpherence_id = $request->getURIData('id'); if (!$conpherence_id) { return new Aphront404Response(); } $need_participants = false; $needed_capabilities = array(PhabricatorPolicyCapability::CAN_VIEW); $action = $request->getStr('action', ConpherenceUpdateActions::METADATA); switch ($action) { case ConpherenceUpdateActions::REMOVE_PERSON: $person_phid = $request->getStr('remove_person'); if ($person_phid != $user->getPHID()) { $needed_capabilities[] = PhabricatorPolicyCapability::CAN_EDIT; } break; case ConpherenceUpdateActions::ADD_PERSON: case ConpherenceUpdateActions::METADATA: $needed_capabilities[] = PhabricatorPolicyCapability::CAN_EDIT; break; - case ConpherenceUpdateActions::JOIN_ROOM: - $needed_capabilities[] = PhabricatorPolicyCapability::CAN_JOIN; - break; case ConpherenceUpdateActions::NOTIFICATIONS: $need_participants = true; break; case ConpherenceUpdateActions::LOAD: break; } $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($conpherence_id)) ->needParticipants($need_participants) ->requireCapabilities($needed_capabilities) ->executeOne(); $latest_transaction_id = null; $response_mode = $request->isAjax() ? 'ajax' : 'redirect'; $error_view = null; $e_file = array(); $errors = array(); $delete_draft = false; $xactions = array(); if ($request->isFormPost() || ($action == ConpherenceUpdateActions::LOAD)) { $editor = id(new ConpherenceEditor()) ->setContinueOnNoEffect($request->isContinueRequest()) ->setContentSourceFromRequest($request) ->setActor($user); switch ($action) { case ConpherenceUpdateActions::DRAFT: $draft = PhabricatorDraft::newFromUserAndKey( $user, $conpherence->getPHID()); $draft->setDraft($request->getStr('text')); $draft->replaceOrDelete(); return new AphrontAjaxResponse(); case ConpherenceUpdateActions::JOIN_ROOM: $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransaction::TYPE_PARTICIPANTS) ->setNewValue(array('+' => array($user->getPHID()))); $delete_draft = true; $message = $request->getStr('text'); if ($message) { $message_xactions = $editor->generateTransactionsFromText( $user, $conpherence, $message); $xactions = array_merge($xactions, $message_xactions); } // for now, just redirect back to the conpherence so everything // will work okay...! $response_mode = 'redirect'; break; case ConpherenceUpdateActions::MESSAGE: $message = $request->getStr('text'); if (strlen($message)) { $xactions = $editor->generateTransactionsFromText( $user, $conpherence, $message); $delete_draft = true; } else { $action = ConpherenceUpdateActions::LOAD; $updated = false; $response_mode = 'ajax'; } break; case ConpherenceUpdateActions::ADD_PERSON: $person_phids = $request->getArr('add_person'); if (!empty($person_phids)) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransaction::TYPE_PARTICIPANTS) ->setNewValue(array('+' => $person_phids)); } break; case ConpherenceUpdateActions::REMOVE_PERSON: if (!$request->isContinueRequest()) { // do nothing; we'll display a confirmation dialog instead break; } $person_phid = $request->getStr('remove_person'); if ($person_phid) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceTransaction::TYPE_PARTICIPANTS) ->setNewValue(array('-' => array($person_phid))); $response_mode = 'go-home'; } break; case ConpherenceUpdateActions::NOTIFICATIONS: $notifications = $request->getStr('notifications'); $participant = $conpherence->getParticipantIfExists($user->getPHID()); if (!$participant) { return id(new Aphront404Response()); } $participant->setSettings(array('notifications' => $notifications)); $participant->save(); return id(new AphrontRedirectResponse()) ->setURI('/'.$conpherence->getMonogram()); break; case ConpherenceUpdateActions::METADATA: $title = $request->getStr('title'); $topic = $request->getStr('topic'); // all other metadata updates are continue requests if (!$request->isContinueRequest()) { break; } $title = $request->getStr('title'); $topic = $request->getStr('topic'); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceThreadTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceThreadTopicTransaction::TRANSACTIONTYPE) ->setNewValue($topic); $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')); if (!$request->getExists('force_ajax')) { $response_mode = 'redirect'; } break; case ConpherenceUpdateActions::LOAD: $updated = false; $response_mode = 'ajax'; break; default: throw new Exception(pht('Unknown action: %s', $action)); break; } if ($xactions) { try { $xactions = $editor->applyTransactions($conpherence, $xactions); if ($delete_draft) { $draft = PhabricatorDraft::newFromUserAndKey( $user, $conpherence->getPHID()); $draft->delete(); } } catch (PhabricatorApplicationTransactionNoEffectException $ex) { return id(new PhabricatorApplicationTransactionNoEffectResponse()) ->setCancelURI($this->getApplicationURI($conpherence_id.'/')) ->setException($ex); } // xactions had no effect...! if (empty($xactions)) { $errors[] = pht( 'That was a non-update. Try cancel.'); } } if ($xactions || ($action == ConpherenceUpdateActions::LOAD)) { switch ($response_mode) { case 'ajax': $latest_transaction_id = $request->getInt('latest_transaction_id'); $content = $this->loadAndRenderUpdates( $action, $conpherence_id, $latest_transaction_id); return id(new AphrontAjaxResponse()) ->setContent($content); break; case 'go-home': $content = array( 'href' => $this->getApplicationURI(), ); return id(new AphrontAjaxResponse()) ->setContent($content); break; case 'redirect': default: return id(new AphrontRedirectResponse()) ->setURI('/'.$conpherence->getMonogram()); break; } } } if ($errors) { $error_view = id(new PHUIInfoView()) ->setErrors($errors); } switch ($action) { case ConpherenceUpdateActions::NOTIFICATIONS: $dialog = $this->renderPreferencesDialog($conpherence); break; case ConpherenceUpdateActions::ADD_PERSON: $dialog = $this->renderAddPersonDialog($conpherence); break; case ConpherenceUpdateActions::REMOVE_PERSON: $dialog = $this->renderRemovePersonDialog($conpherence); break; case ConpherenceUpdateActions::METADATA: default: $dialog = $this->renderMetadataDialog($conpherence, $error_view); break; } return $dialog ->setUser($user) ->setWidth(AphrontDialogView::WIDTH_FORM) ->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/')) ->addSubmitButton() ->addCancelButton($this->getApplicationURI($conpherence->getID().'/')); } private function renderPreferencesDialog( ConpherenceThread $conpherence) { $request = $this->getRequest(); $user = $request->getUser(); $participant = $conpherence->getParticipantIfExists($user->getPHID()); if (!$participant) { - $can_join = PhabricatorPolicyFilter::hasCapability( - $user, - $conpherence, - PhabricatorPolicyCapability::CAN_JOIN); - if ($can_join) { + if ($user->isLoggedIn()) { $text = pht( 'Notification settings are available after joining the room.'); - } else if ($user->isLoggedIn()) { - $text = pht( - 'Notification settings not applicable to rooms you can not join.'); } else { $text = pht( 'Notification settings are available after logging in and joining '. 'the room.'); } return id(new AphrontDialogView()) ->setTitle(pht('Room Preferences')) ->appendParagraph($text); } $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; $notification_default = $user->getUserSetting($notification_key); $settings = $participant->getSettings(); $notifications = idx( $settings, 'notifications', $notification_default); $form = id(new AphrontFormView()) ->setUser($user) ->setFullWidth(true) ->appendControl( id(new AphrontFormRadioButtonControl()) ->addButton( PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL, PhabricatorConpherenceNotificationsSetting::getSettingLabel( PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL), '') ->addButton( PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY, PhabricatorConpherenceNotificationsSetting::getSettingLabel( PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY), '') ->setName('notifications') ->setValue($notifications)); return id(new AphrontDialogView()) ->setTitle(pht('Room Preferences')) ->addHiddenInput('action', 'notifications') ->addHiddenInput( 'latest_transaction_id', $request->getInt('latest_transaction_id')) ->appendForm($form); } private function renderAddPersonDialog( ConpherenceThread $conpherence) { $request = $this->getRequest(); $user = $request->getUser(); $add_person = $request->getStr('add_person'); $form = id(new AphrontFormView()) ->setUser($user) ->setFullWidth(true) ->appendControl( id(new AphrontFormTokenizerControl()) ->setName('add_person') ->setUser($user) ->setDatasource(new PhabricatorPeopleDatasource())); $view = id(new AphrontDialogView()) ->setTitle(pht('Add Participants')) ->addHiddenInput('action', 'add_person') ->addHiddenInput( 'latest_transaction_id', $request->getInt('latest_transaction_id')) ->appendForm($form); return $view; } private function renderRemovePersonDialog( ConpherenceThread $conpherence) { $request = $this->getRequest(); $viewer = $request->getUser(); $remove_person = $request->getStr('remove_person'); $participants = $conpherence->getParticipants(); $removed_user = id(new PhabricatorPeopleQuery()) ->setViewer($viewer) ->withPHIDs(array($remove_person)) ->executeOne(); if (!$removed_user) { return new Aphront404Response(); } $is_self = ($viewer->getPHID() == $removed_user->getPHID()); $is_last = (count($participants) == 1); $test_conpherence = clone $conpherence; $test_conpherence->attachParticipants(array()); $still_visible = PhabricatorPolicyFilter::hasCapability( $removed_user, $test_conpherence, PhabricatorPolicyCapability::CAN_VIEW); $body = array(); if ($is_self) { $title = pht('Leave Room'); $body[] = pht( 'Are you sure you want to leave this room?'); } else { $title = pht('Banish User'); $body[] = pht( 'Banish %s from the realm?', phutil_tag('strong', array(), $removed_user->getUsername())); } if ($still_visible) { if ($is_self) { $body[] = pht( 'You will be able to rejoin the room later.'); } else { $body[] = pht( 'This user will be able to rejoin the room later.'); } } else { if ($is_self) { if ($is_last) { $body[] = pht( 'You are the last member, so you will never be able to rejoin '. 'the room.'); } else { $body[] = pht( 'You will not be able to rejoin the room on your own, but '. 'someone else can invite you later.'); } } else { $body[] = pht( 'This user will not be able to rejoin the room unless invited '. 'again.'); } } $dialog = id(new AphrontDialogView()) ->setTitle($title) ->addHiddenInput('action', 'remove_person') ->addHiddenInput('remove_person', $remove_person) ->addHiddenInput( 'latest_transaction_id', $request->getInt('latest_transaction_id')) ->addHiddenInput('__continue__', true); foreach ($body as $paragraph) { $dialog->appendParagraph($paragraph); } return $dialog; } private function renderMetadataDialog( ConpherenceThread $conpherence, $error_view) { $request = $this->getRequest(); $user = $request->getUser(); $title = pht('Update Room'); $form = id(new PHUIFormLayoutView()) ->appendChild($error_view) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Title')) ->setName('title') ->setValue($conpherence->getTitle())) ->appendChild( id(new AphrontFormTextControl()) ->setLabel(pht('Topic')) ->setName('topic') ->setValue($conpherence->getTopic())); $policies = id(new PhabricatorPolicyQuery()) ->setViewer($user) ->setObject($conpherence) ->execute(); $form ->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)); $view = id(new AphrontDialogView()) ->setTitle($title) ->addHiddenInput('action', 'metadata') ->addHiddenInput( 'latest_transaction_id', $request->getInt('latest_transaction_id')) ->addHiddenInput('__continue__', true) ->appendChild($form); if ($request->getExists('force_ajax')) { $view->addHiddenInput('force_ajax', true); } return $view; } private function loadAndRenderUpdates( $action, $conpherence_id, $latest_transaction_id) { $need_transactions = false; $need_participant_cache = true; switch ($action) { case ConpherenceUpdateActions::METADATA: case ConpherenceUpdateActions::LOAD: $need_transactions = true; break; case ConpherenceUpdateActions::MESSAGE: case ConpherenceUpdateActions::ADD_PERSON: $need_transactions = true; break; case ConpherenceUpdateActions::REMOVE_PERSON: case ConpherenceUpdateActions::NOTIFICATIONS: default: break; } $user = $this->getRequest()->getUser(); $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->setAfterTransactionID($latest_transaction_id) ->needProfileImage(true) ->needParticipantCache($need_participant_cache) ->needParticipants(true) ->needTransactions($need_transactions) ->withIDs(array($conpherence_id)) ->executeOne(); $non_update = false; if ($need_transactions && $conpherence->getTransactions()) { $data = ConpherenceTransactionRenderer::renderTransactions( $user, $conpherence); $key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY; $minimized = $user->getUserSetting($key); if (!$minimized) { $participant_obj = $conpherence->getParticipant($user->getPHID()); $participant_obj ->markUpToDate($conpherence, $data['latest_transaction']); } } else if ($need_transactions) { $non_update = true; $data = array(); } else { $data = array(); } $rendered_transactions = idx($data, 'transactions'); $new_latest_transaction_id = idx($data, 'latest_transaction_id'); $update_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); $nav_item = null; $header = null; $people_widget = null; switch ($action) { case ConpherenceUpdateActions::METADATA: $policy_objects = id(new PhabricatorPolicyQuery()) ->setViewer($user) ->setObject($conpherence) ->execute(); $header = $this->buildHeaderPaneContent( $conpherence, $policy_objects); $header = hsprintf('%s', $header); $nav_item = id(new ConpherenceThreadListView()) ->setUser($user) ->setBaseURI($this->getApplicationURI()) ->renderSingleThread($conpherence, $policy_objects); $nav_item = hsprintf('%s', $nav_item); break; case ConpherenceUpdateActions::ADD_PERSON: $people_widget = id(new ConpherenceParticipantView()) ->setUser($user) ->setConpherence($conpherence) ->setUpdateURI($update_uri); $people_widget = hsprintf('%s', $people_widget->render()); break; case ConpherenceUpdateActions::REMOVE_PERSON: case ConpherenceUpdateActions::NOTIFICATIONS: default: break; } $data = $conpherence->getDisplayData($user); $dropdown_query = id(new AphlictDropdownDataQuery()) ->setViewer($user); $dropdown_query->execute(); $content = array( 'non_update' => $non_update, 'transactions' => hsprintf('%s', $rendered_transactions), 'conpherence_title' => (string)$data['title'], 'latest_transaction_id' => $new_latest_transaction_id, 'nav_item' => $nav_item, 'conpherence_phid' => $conpherence->getPHID(), 'header' => $header, 'people_widget' => $people_widget, 'aphlictDropdownData' => array( $dropdown_query->getNotificationData(), $dropdown_query->getConpherenceData(), ), ); return $content; } } diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php index 1fbb338822..57fe194845 100644 --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -1,253 +1,245 @@ getUser(); $conpherence_id = $request->getURIData('id'); if (!$conpherence_id) { return new Aphront404Response(); } $query = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($conpherence_id)) ->needProfileImage(true) ->needParticipantCache(true) ->needTransactions(true) ->setTransactionLimit($this->getMainQueryLimit()); $before_transaction_id = $request->getInt('oldest_transaction_id'); $after_transaction_id = $request->getInt('newest_transaction_id'); $old_message_id = $request->getURIData('messageID'); if ($before_transaction_id && ($old_message_id || $after_transaction_id)) { throw new Aphront400Response(); } if ($old_message_id && $after_transaction_id) { throw new Aphront400Response(); } $marker_type = 'older'; if ($before_transaction_id) { $query ->setBeforeTransactionID($before_transaction_id); } if ($old_message_id) { $marker_type = 'olderandnewer'; $query ->setAfterTransactionID($old_message_id - 1); } if ($after_transaction_id) { $marker_type = 'newer'; $query ->setAfterTransactionID($after_transaction_id); } $conpherence = $query->executeOne(); if (!$conpherence) { return new Aphront404Response(); } $this->setConpherence($conpherence); $transactions = $this->getNeededTransactions( $conpherence, $old_message_id); $latest_transaction = head($transactions); $participant = $conpherence->getParticipantIfExists($user->getPHID()); if ($participant) { if (!$participant->isUpToDate($conpherence)) { $write_guard = AphrontWriteGuard::beginScopedUnguardedWrites(); $participant->markUpToDate($conpherence, $latest_transaction); $user->clearCacheData(PhabricatorUserMessageCountCacheType::KEY_COUNT); unset($write_guard); } } $data = ConpherenceTransactionRenderer::renderTransactions( $user, $conpherence, $marker_type); $messages = ConpherenceTransactionRenderer::renderMessagePaneContent( $data['transactions'], $data['oldest_transaction_id'], $data['newest_transaction_id']); if ($before_transaction_id || $after_transaction_id) { $header = null; $form = null; $content = array('transactions' => $messages); } else { $policy_objects = id(new PhabricatorPolicyQuery()) ->setViewer($user) ->setObject($conpherence) ->execute(); $header = $this->buildHeaderPaneContent($conpherence, $policy_objects); $search = $this->buildSearchForm(); $form = $this->renderFormContent(); $content = array( 'header' => $header, 'search' => $search, 'transactions' => $messages, 'form' => $form, ); } $d_data = $conpherence->getDisplayData($user); $content['title'] = $title = $d_data['title']; if ($request->isAjax()) { $dropdown_query = id(new AphlictDropdownDataQuery()) ->setViewer($user); $dropdown_query->execute(); $content['threadID'] = $conpherence->getID(); $content['threadPHID'] = $conpherence->getPHID(); $content['latestTransactionID'] = $data['latest_transaction_id']; $content['canEdit'] = PhabricatorPolicyFilter::hasCapability( $user, $conpherence, PhabricatorPolicyCapability::CAN_EDIT); $content['aphlictDropdownData'] = array( $dropdown_query->getNotificationData(), $dropdown_query->getConpherenceData(), ); return id(new AphrontAjaxResponse())->setContent($content); } - $can_join = PhabricatorPolicyFilter::hasCapability( - $user, - $conpherence, - PhabricatorPolicyCapability::CAN_JOIN); - $layout = id(new ConpherenceLayoutView()) ->setUser($user) ->setBaseURI($this->getApplicationURI()) ->setThread($conpherence) ->setHeader($header) ->setSearch($search) ->setMessages($messages) ->setReplyForm($form) ->setLatestTransactionID($data['latest_transaction_id']) ->setRole('thread'); $participating = $conpherence->getParticipantIfExists($user->getPHID()); if (!$user->isLoggedIn()) { $layout->addClass('conpherence-no-pontificate'); } return $this->newPage() ->setTitle($title) ->setPageObjectPHIDs(array($conpherence->getPHID())) ->appendChild($layout); } private function renderFormContent() { $conpherence = $this->getConpherence(); $user = $this->getRequest()->getUser(); - $can_join = PhabricatorPolicyFilter::hasCapability( - $user, - $conpherence, - PhabricatorPolicyCapability::CAN_JOIN); + $participating = $conpherence->getParticipantIfExists($user->getPHID()); - if (!$can_join && !$participating && $user->isLoggedIn()) { + if (!$participating && $user->isLoggedIn()) { return null; } $draft = PhabricatorDraft::newFromUserAndKey( $user, $conpherence->getPHID()); $update_uri = $this->getApplicationURI('update/'.$conpherence->getID().'/'); if ($user->isLoggedIn()) { $this->initBehavior('conpherence-pontificate'); if ($participating) { $action = ConpherenceUpdateActions::MESSAGE; $status = new PhabricatorNotificationStatusView(); } else { $action = ConpherenceUpdateActions::JOIN_ROOM; $status = pht('Sending a message will also join the room.'); } $form = id(new AphrontFormView()) ->setUser($user) ->setAction($update_uri) ->addSigil('conpherence-pontificate') ->setWorkflow(true) ->addHiddenInput('action', $action) ->appendChild( id(new PhabricatorRemarkupControl()) ->setUser($user) ->setName('text') ->setSendOnEnter(true) ->setValue($draft->getDraft())); $status_view = phutil_tag( 'div', array( 'class' => 'conpherence-room-status', 'id' => 'conpherence-room-status', ), $status); $view = phutil_tag_div( 'pontificate-container', array($form, $status_view)); return $view; } else { // user not logged in so give them a login button. $login_href = id(new PhutilURI('/auth/start/')) ->setQueryParam('next', '/'.$conpherence->getMonogram()); return id(new PHUIFormLayoutView()) ->addClass('login-to-participate') ->appendInstructions(pht('Log in to join this room and participate.')) ->appendChild( id(new PHUIButtonView()) ->setTag('a') ->setText(pht('Login to Participate')) ->setHref((string)$login_href)); } } private function getNeededTransactions( ConpherenceThread $conpherence, $message_id) { if ($message_id) { $newer_transactions = $conpherence->getTransactions(); $query = id(new ConpherenceTransactionQuery()) ->setViewer($this->getRequest()->getUser()) ->withObjectPHIDs(array($conpherence->getPHID())) ->setAfterID($message_id) ->needHandles(true) ->setLimit(self::OLDER_FETCH_LIMIT); $older_transactions = $query->execute(); $handles = array(); foreach ($older_transactions as $transaction) { $handles += $transaction->getHandles(); } $conpherence->attachHandles($conpherence->getHandles() + $handles); $transactions = array_merge($newer_transactions, $older_transactions); $conpherence->attachTransactions($transactions); } else { $transactions = $conpherence->getTransactions(); } return $transactions; } private function getMainQueryLimit() { $request = $this->getRequest(); $base_limit = ConpherenceThreadQuery::TRANSACTION_LIMIT; if ($request->getURIData('messageID')) { $base_limit = $base_limit - self::OLDER_FETCH_LIMIT; } return $base_limit; } } diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index 2026eb94c4..e4728f3c00 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -1,569 +1,556 @@ getPHID(); $participant_phids = array_unique($participant_phids); } if (empty($message)) { $errors[] = self::ERROR_EMPTY_MESSAGE; } if (!$errors) { $xactions = array(); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS) ->setNewValue(array('+' => $participant_phids)); if ($title) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceThreadTitleTransaction::TRANSACTIONTYPE) ->setNewValue($title); } if (strlen($topic)) { $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType( ConpherenceThreadTopicTransaction::TRANSACTIONTYPE) ->setNewValue($topic); } $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new ConpherenceTransactionComment()) ->setContent($message) ->setConpherencePHID($conpherence->getPHID())); id(new ConpherenceEditor()) ->setActor($creator) ->setContentSource($source) ->setContinueOnNoEffect(true) ->applyTransactions($conpherence, $xactions); } return array($errors, $conpherence); } public function generateTransactionsFromText( PhabricatorUser $viewer, ConpherenceThread $conpherence, $text) { $xactions = array(); $xactions[] = id(new ConpherenceTransaction()) ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) ->attachComment( id(new ConpherenceTransactionComment()) ->setContent($text) ->setConpherencePHID($conpherence->getPHID())); return $xactions; } public function getTransactionTypes() { $types = parent::getTransactionTypes(); $types[] = ConpherenceTransaction::TYPE_PARTICIPANTS; $types[] = PhabricatorTransactions::TYPE_COMMENT; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; - $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; return $types; } public function getCreateObjectTitle($author, $object) { return pht('%s created this room.', $author); } protected function getCustomTransactionOldValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PARTICIPANTS: if ($this->getIsNewObject()) { return array(); } return $object->getParticipantPHIDs(); } } protected function getCustomTransactionNewValue( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PARTICIPANTS: return $this->getPHIDTransactionNewValue($xaction); } } /** * We really only need a read lock if we have a comment. In that case, we * must update the messagesCount field on the conpherence and * seenMessagesCount(s) for the participant(s). */ protected function shouldReadLock( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $lock = false; switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: $lock = true; break; } return $lock; } /** * We need to apply initial effects IFF the conpherence is new. We must * save the conpherence first thing to make sure we have an id and a phid, as * well as create the initial set of participants so that we pass policy * checks. */ protected function shouldApplyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { return $this->getIsNewObject(); } protected function applyInitialEffects( PhabricatorLiskDAO $object, array $xactions) { $object->save(); foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PARTICIPANTS: // Since this is a new ConpherenceThread, we have to create the // participation data asap to pass policy checks. For existing // ConpherenceThreads, the existing participation is correct // at this stage. Note that later in applyCustomExternalTransaction // this participation data will be updated, particularly the // behindTransactionPHID which is just a generated dummy for now. $participants = array(); $phids = $this->getPHIDTransactionNewValue($xaction, array()); foreach ($phids as $phid) { if ($phid == $this->getActor()->getPHID()) { $status = ConpherenceParticipationStatus::UP_TO_DATE; $message_count = 1; } else { $status = ConpherenceParticipationStatus::BEHIND; $message_count = 0; } $participants[$phid] = id(new ConpherenceParticipant()) ->setConpherencePHID($object->getPHID()) ->setParticipantPHID($phid) ->setParticipationStatus($status) ->setDateTouched(time()) ->setBehindTransactionPHID($xaction->generatePHID()) ->setSeenMessageCount($message_count) ->save(); $object->attachParticipants($participants); $object->setRecentParticipantPHIDs(array_keys($participants)); } break; } } } protected function applyCustomInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $make_author_recent_participant = true; switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PARTICIPANTS: if (!$this->getIsNewObject()) { $old_map = array_fuse($xaction->getOldValue()); $new_map = array_fuse($xaction->getNewValue()); // if we added people, add them to the end of "recent" participants $add = array_keys(array_diff_key($new_map, $old_map)); // if we remove people, then definintely remove them from "recent" // participants $del = array_keys(array_diff_key($old_map, $new_map)); if ($add || $del) { $participants = $object->getRecentParticipantPHIDs(); if ($add) { $participants = array_merge($participants, $add); } if ($del) { $participants = array_diff($participants, $del); $actor = $this->requireActor(); if (in_array($actor->getPHID(), $del)) { $make_author_recent_participant = false; } } $participants = array_slice(array_unique($participants), 0, 10); $object->setRecentParticipantPHIDs($participants); } } break; } if ($make_author_recent_participant) { $this->makeAuthorMostRecentParticipant($object, $xaction); } } protected function applyBuiltinInternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: $object->setMessageCount((int)$object->getMessageCount() + 1); break; } return parent::applyBuiltinInternalTransaction($object, $xaction); } private function makeAuthorMostRecentParticipant( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { $participants = $object->getRecentParticipantPHIDs(); array_unshift($participants, $xaction->getAuthorPHID()); $participants = array_slice(array_unique($participants), 0, 10); $object->setRecentParticipantPHIDs($participants); } protected function applyCustomExternalTransaction( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PARTICIPANTS: if ($this->getIsNewObject()) { continue; } $participants = $object->getParticipants(); $old_map = array_fuse($xaction->getOldValue()); $new_map = array_fuse($xaction->getNewValue()); $remove = array_keys(array_diff_key($old_map, $new_map)); foreach ($remove as $phid) { $remove_participant = $participants[$phid]; $remove_participant->delete(); unset($participants[$phid]); } $add = array_keys(array_diff_key($new_map, $old_map)); foreach ($add as $phid) { if ($phid == $this->getActor()->getPHID()) { $status = ConpherenceParticipationStatus::UP_TO_DATE; $message_count = $object->getMessageCount(); } else { $status = ConpherenceParticipationStatus::BEHIND; $message_count = 0; } $participants[$phid] = id(new ConpherenceParticipant()) ->setConpherencePHID($object->getPHID()) ->setParticipantPHID($phid) ->setParticipationStatus($status) ->setDateTouched(time()) ->setBehindTransactionPHID($xaction->getPHID()) ->setSeenMessageCount($message_count) ->save(); } $object->attachParticipants($participants); break; } } protected function applyFinalEffects( PhabricatorLiskDAO $object, array $xactions) { if (!$xactions) { return $xactions; } $message_count = 0; foreach ($xactions as $xaction) { switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: $message_count++; // update everyone's participation status on a message -only- $xaction_phid = $xaction->getPHID(); $behind = ConpherenceParticipationStatus::BEHIND; $up_to_date = ConpherenceParticipationStatus::UP_TO_DATE; $participants = $object->getParticipants(); $user = $this->getActor(); $time = time(); foreach ($participants as $phid => $participant) { if ($phid != $user->getPHID()) { if ($participant->getParticipationStatus() != $behind) { $participant->setBehindTransactionPHID($xaction_phid); $participant->setSeenMessageCount( $object->getMessageCount() - $message_count); } $participant->setParticipationStatus($behind); $participant->setDateTouched($time); } else { $participant->setSeenMessageCount($object->getMessageCount()); $participant->setBehindTransactionPHID($xaction_phid); $participant->setParticipationStatus($up_to_date); $participant->setDateTouched($time); } $participant->save(); } PhabricatorUserCache::clearCaches( PhabricatorUserMessageCountCacheType::KEY_COUNT, array_keys($participants)); break; } } if ($xactions) { $data = array( 'type' => 'message', 'threadPHID' => $object->getPHID(), 'messageID' => last($xactions)->getID(), 'subscribers' => array($object->getPHID()), ); PhabricatorNotificationClient::tryToPostMessage($data); } return $xactions; } protected function requireCapabilities( PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { parent::requireCapabilities($object, $xaction); switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PARTICIPANTS: $old_map = array_fuse($xaction->getOldValue()); $new_map = array_fuse($xaction->getNewValue()); $add = array_keys(array_diff_key($new_map, $old_map)); $rem = array_keys(array_diff_key($old_map, $new_map)); $actor_phid = $this->requireActor()->getPHID(); - $is_join = (($add === array($actor_phid)) && !$rem); - $is_leave = (($rem === array($actor_phid)) && !$add); - - if ($is_join) { - // You need CAN_JOIN to join a room. - PhabricatorPolicyFilter::requireCapability( - $this->requireActor(), - $object, - PhabricatorPolicyCapability::CAN_JOIN); - } else if ($is_leave) { - // You don't need any capabilities to leave a conpherence thread. - } else { - // You need CAN_EDIT to change participants other than yourself. - PhabricatorPolicyFilter::requireCapability( - $this->requireActor(), - $object, - PhabricatorPolicyCapability::CAN_EDIT); - } + // You need CAN_EDIT to change participants other than yourself. + PhabricatorPolicyFilter::requireCapability( + $this->requireActor(), + $object, + PhabricatorPolicyCapability::CAN_EDIT); + break; case ConpherenceThreadTitleTransaction::TRANSACTIONTYPE: case ConpherenceThreadTopicTransaction::TRANSACTIONTYPE: PhabricatorPolicyFilter::requireCapability( $this->requireActor(), $object, PhabricatorPolicyCapability::CAN_EDIT); break; } } protected function mergeTransactions( PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $v) { $type = $u->getTransactionType(); switch ($type) { case ConpherenceTransaction::TYPE_PARTICIPANTS: return $this->mergePHIDOrEdgeTransactions($u, $v); } return parent::mergeTransactions($u, $v); } protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { return true; } protected function buildReplyHandler(PhabricatorLiskDAO $object) { return id(new ConpherenceReplyHandler()) ->setActor($this->getActor()) ->setMailReceiver($object); } protected function buildMailTemplate(PhabricatorLiskDAO $object) { $id = $object->getID(); $title = $object->getTitle(); if (!$title) { $title = pht( '%s sent you a message.', $this->getActor()->getUserName()); } $phid = $object->getPHID(); return id(new PhabricatorMetaMTAMail()) ->setSubject("Z{$id}: {$title}") ->addHeader('Thread-Topic', "Z{$id}: {$phid}"); } protected function getMailTo(PhabricatorLiskDAO $object) { $to_phids = array(); $participants = $object->getParticipants(); if (!$participants) { return $to_phids; } $participant_phids = mpull($participants, 'getParticipantPHID'); $users = id(new PhabricatorPeopleQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withPHIDs($participant_phids) ->needUserSettings(true) ->execute(); $users = mpull($users, null, 'getPHID'); $notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY; $notification_email = PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL; foreach ($participants as $phid => $participant) { $user = idx($users, $phid); if ($user) { $default = $user->getUserSetting($notification_key); } else { $default = $notification_email; } $settings = $participant->getSettings(); $notifications = idx($settings, 'notifications', $default); if ($notifications == $notification_email) { $to_phids[] = $phid; } } return $to_phids; } protected function getMailCC(PhabricatorLiskDAO $object) { return array(); } protected function buildMailBody( PhabricatorLiskDAO $object, array $xactions) { $body = parent::buildMailBody($object, $xactions); $body->addLinkSection( pht('CONPHERENCE DETAIL'), PhabricatorEnv::getProductionURI('/'.$object->getMonogram())); return $body; } protected function addEmailPreferenceSectionToMailBody( PhabricatorMetaMTAMailBody $body, PhabricatorLiskDAO $object, array $xactions) { $href = PhabricatorEnv::getProductionURI( '/'.$object->getMonogram().'?settings'); $label = pht('EMAIL PREFERENCES FOR THIS ROOM'); $body->addLinkSection($label, $href); } protected function getMailSubjectPrefix() { return PhabricatorEnv::getEnvConfig('metamta.conpherence.subject-prefix'); } protected function supportsSearch() { return true; } protected function validateTransaction( PhabricatorLiskDAO $object, $type, array $xactions) { $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { case ConpherenceTransaction::TYPE_PARTICIPANTS: foreach ($xactions as $xaction) { $new_phids = $this->getPHIDTransactionNewValue($xaction, array()); $old_phids = nonempty($object->getParticipantPHIDs(), array()); $phids = array_diff($new_phids, $old_phids); if (!$phids) { continue; } $users = id(new PhabricatorPeopleQuery()) ->setViewer($this->requireActor()) ->withPHIDs($phids) ->execute(); $users = mpull($users, null, 'getPHID'); foreach ($phids as $phid) { if (isset($users[$phid])) { continue; } $errors[] = new PhabricatorApplicationTransactionValidationError( $type, pht('Invalid'), pht('New room participant "%s" is not a valid user.', $phid), $xaction); } } break; } return $errors; } } diff --git a/src/applications/conpherence/storage/ConpherenceThread.php b/src/applications/conpherence/storage/ConpherenceThread.php index 17a3de6c50..db6d4a7e61 100644 --- a/src/applications/conpherence/storage/ConpherenceThread.php +++ b/src/applications/conpherence/storage/ConpherenceThread.php @@ -1,420 +1,416 @@ getObjectPolicyFullKey(); return id(new ConpherenceThread()) ->setMessageCount(0) ->setTitle('') ->setTopic('') ->attachParticipants(array()) ->setViewPolicy($default_policy) ->setEditPolicy($default_policy) - ->setJoinPolicy($default_policy); + ->setJoinPolicy(''); } protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, self::CONFIG_SERIALIZATION => array( 'recentParticipantPHIDs' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'title' => 'text255?', 'topic' => 'text255', 'messageCount' => 'uint64', 'mailKey' => 'text20', 'joinPolicy' => 'policy', 'profileImagePHID' => 'phid?', ), self::CONFIG_KEY_SCHEMA => array( 'key_phid' => null, 'phid' => array( 'columns' => array('phid'), 'unique' => true, ), ), ) + parent::getConfiguration(); } public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorConpherenceThreadPHIDType::TYPECONST); } public function save() { if (!$this->getMailKey()) { $this->setMailKey(Filesystem::readRandomCharacters(20)); } return parent::save(); } public function getMonogram() { return 'Z'.$this->getID(); } public function attachParticipants(array $participants) { assert_instances_of($participants, 'ConpherenceParticipant'); $this->participants = $participants; return $this; } public function getParticipants() { return $this->assertAttached($this->participants); } public function getParticipant($phid) { $participants = $this->getParticipants(); return $participants[$phid]; } public function getParticipantIfExists($phid, $default = null) { $participants = $this->getParticipants(); return idx($participants, $phid, $default); } public function getParticipantPHIDs() { $participants = $this->getParticipants(); return array_keys($participants); } public function attachHandles(array $handles) { assert_instances_of($handles, 'PhabricatorObjectHandle'); $this->handles = $handles; return $this; } public function getHandles() { return $this->assertAttached($this->handles); } public function attachTransactions(array $transactions) { assert_instances_of($transactions, 'ConpherenceTransaction'); $this->transactions = $transactions; return $this; } public function getTransactions($assert_attached = true) { return $this->assertAttached($this->transactions); } public function hasAttachedTransactions() { return $this->transactions !== self::ATTACHABLE; } public function getTransactionsFrom($begin = 0, $amount = null) { $length = count($this->transactions); return array_slice( $this->getTransactions(), $length - $begin - $amount, $amount); } public function getProfileImageURI() { return $this->getProfileImageFile()->getBestURI(); } public function attachProfileImageFile(PhabricatorFile $file) { $this->profileImageFile = $file; return $this; } public function getProfileImageFile() { return $this->assertAttached($this->profileImageFile); } /** * Get a thread title which doesn't require handles to be attached. * * This is a less rich title than @{method:getDisplayTitle}, but does not * require handles to be attached. We use it to build thread handles without * risking cycles or recursion while querying. * * @return string Lower quality human-readable title. */ public function getStaticTitle() { $title = $this->getTitle(); if (strlen($title)) { return $title; } return pht('Private Room'); } /** * Get the thread's display title for a user. * * If a thread doesn't have a title set, this will return a string describing * recent participants. * * @param PhabricatorUser Viewer. * @return string Thread title. */ public function getDisplayTitle(PhabricatorUser $viewer) { $title = $this->getTitle(); if (strlen($title)) { return $title; } return $this->getRecentParticipantsString($viewer); } /** * Get recent participants (other than the viewer) as a string. * * For example, this method might return "alincoln, htaft, gwashington...". * * @param PhabricatorUser Viewer. * @return string Description of other participants. */ private function getRecentParticipantsString(PhabricatorUser $viewer) { $handles = $this->getHandles(); $phids = $this->getOtherRecentParticipantPHIDs($viewer); if (count($phids) == 0) { $phids[] = $viewer->getPHID(); $more = false; } else { $limit = 3; $more = (count($phids) > $limit); $phids = array_slice($phids, 0, $limit); } $names = array_select_keys($handles, $phids); $names = mpull($names, 'getName'); $names = implode(', ', $names); if ($more) { $names = $names.'...'; } return $names; } /** * Get PHIDs for recent participants who are not the viewer. * * @param PhabricatorUser Viewer. * @return list Participants who are not the viewer. */ private function getOtherRecentParticipantPHIDs(PhabricatorUser $viewer) { $phids = $this->getRecentParticipantPHIDs(); $phids = array_fuse($phids); unset($phids[$viewer->getPHID()]); return array_values($phids); } public function getDisplayData(PhabricatorUser $viewer) { $handles = $this->getHandles(); if ($this->hasAttachedTransactions()) { $transactions = $this->getTransactions(); } else { $transactions = array(); } $img_src = $this->getProfileImageURI(); $message_transaction = null; foreach ($transactions as $transaction) { if ($message_transaction) { break; } switch ($transaction->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: $message_transaction = $transaction; break; default: break; } } if ($message_transaction) { $message_handle = $handles[$message_transaction->getAuthorPHID()]; $subtitle = sprintf( '%s: %s', $message_handle->getName(), id(new PhutilUTF8StringTruncator()) ->setMaximumGlyphs(60) ->truncateString( $message_transaction->getComment()->getContent())); } else { // Kinda lame, but maybe add last message to cache? $subtitle = pht('No recent messages'); } $user_participation = $this->getParticipantIfExists($viewer->getPHID()); if ($user_participation) { $user_seen_count = $user_participation->getSeenMessageCount(); } else { $user_seen_count = 0; } $unread_count = $this->getMessageCount() - $user_seen_count; $title = $this->getDisplayTitle($viewer); $topic = $this->getTopic(); return array( 'title' => $title, 'topic' => $topic, 'subtitle' => $subtitle, 'unread_count' => $unread_count, 'epoch' => $this->getDateModified(), 'image' => $img_src, ); } /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ public function getCapabilities() { return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, - PhabricatorPolicyCapability::CAN_JOIN, ); } public function getPolicy($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); - case PhabricatorPolicyCapability::CAN_JOIN: - return $this->getJoinPolicy(); } return PhabricatorPolicies::POLICY_NOONE; } public function hasAutomaticCapability($capability, PhabricatorUser $user) { // this bad boy isn't even created yet so go nuts $user if (!$this->getID()) { return true; } switch ($capability) { case PhabricatorPolicyCapability::CAN_EDIT: - case PhabricatorPolicyCapability::CAN_JOIN: return false; } $participants = $this->getParticipants(); return isset($participants[$user->getPHID()]); } public function describeAutomaticCapability($capability) { switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return pht('Participants in a room can always view it.'); break; } } public static function loadViewPolicyObjects( PhabricatorUser $viewer, array $conpherences) { assert_instances_of($conpherences, __CLASS__); $policies = array(); foreach ($conpherences as $room) { $policies[$room->getViewPolicy()] = 1; } $policy_objects = array(); if ($policies) { $policy_objects = id(new PhabricatorPolicyQuery()) ->setViewer($viewer) ->withPHIDs(array_keys($policies)) ->execute(); } return $policy_objects; } public function getPolicyIconName(array $policy_objects) { assert_instances_of($policy_objects, 'PhabricatorPolicy'); $icon = $policy_objects[$this->getViewPolicy()]->getIcon(); return $icon; } /* -( PhabricatorApplicationTransactionInterface )------------------------- */ public function getApplicationTransactionEditor() { return new ConpherenceEditor(); } public function getApplicationTransactionObject() { return $this; } public function getApplicationTransactionTemplate() { return new ConpherenceTransaction(); } public function willRenderTimeline( PhabricatorApplicationTransactionView $timeline, AphrontRequest $request) { return $timeline; } /* -( PhabricatorNgramInterface )------------------------------------------ */ public function newNgrams() { return array( id(new ConpherenceThreadTitleNgrams()) ->setValue($this->getTitle()), ); } /* -( PhabricatorDestructibleInterface )----------------------------------- */ public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { $this->openTransaction(); $this->delete(); $participants = id(new ConpherenceParticipant()) ->loadAllWhere('conpherencePHID = %s', $this->getPHID()); foreach ($participants as $participant) { $participant->delete(); } $this->saveTransaction(); } } diff --git a/src/applications/conpherence/storage/ConpherenceTransaction.php b/src/applications/conpherence/storage/ConpherenceTransaction.php index b449037235..c692dffb98 100644 --- a/src/applications/conpherence/storage/ConpherenceTransaction.php +++ b/src/applications/conpherence/storage/ConpherenceTransaction.php @@ -1,142 +1,106 @@ getTransactionType()) { case self::TYPE_PARTICIPANTS: return pht( 'You can not add a participant who has already been added.'); break; } return parent::getNoEffectDescription(); } public function shouldHide() { $old = $this->getOldValue(); switch ($this->getTransactionType()) { case self::TYPE_PARTICIPANTS: return ($old === null); case self::TYPE_DATE_MARKER: return false; } return parent::shouldHide(); } public function getTitle() { $author_phid = $this->getAuthorPHID(); $old = $this->getOldValue(); $new = $this->getNewValue(); switch ($this->getTransactionType()) { - case PhabricatorTransactions::TYPE_VIEW_POLICY: - case PhabricatorTransactions::TYPE_EDIT_POLICY: - case PhabricatorTransactions::TYPE_JOIN_POLICY: - return $this->getRoomTitle(); - break; case self::TYPE_PARTICIPANTS: $add = array_diff($new, $old); $rem = array_diff($old, $new); if ($add && $rem) { $title = pht( '%s edited participant(s), added %d: %s; removed %d: %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add), count($rem), $this->renderHandleList($rem)); } else if ($add) { $title = pht( '%s added %d participant(s): %s.', $this->renderHandleLink($author_phid), count($add), $this->renderHandleList($add)); } else { $title = pht( '%s removed %d participant(s): %s.', $this->renderHandleLink($author_phid), count($rem), $this->renderHandleList($rem)); } return $title; break; } return parent::getTitle(); } - private function getRoomTitle() { - $author_phid = $this->getAuthorPHID(); - - $old = $this->getOldValue(); - $new = $this->getNewValue(); - - switch ($this->getTransactionType()) { - case PhabricatorTransactions::TYPE_VIEW_POLICY: - return pht( - '%s changed the visibility of this room from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderPolicyName($old, 'old'), - $this->renderPolicyName($new, 'new')); - break; - case PhabricatorTransactions::TYPE_EDIT_POLICY: - return pht( - '%s changed the edit policy of this room from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderPolicyName($old, 'old'), - $this->renderPolicyName($new, 'new')); - break; - case PhabricatorTransactions::TYPE_JOIN_POLICY: - return pht( - '%s changed the join policy of this room from "%s" to "%s".', - $this->renderHandleLink($author_phid), - $this->renderPolicyName($old, 'old'), - $this->renderPolicyName($new, 'new')); - break; - } - } - public function getRequiredHandlePHIDs() { $phids = parent::getRequiredHandlePHIDs(); $old = $this->getOldValue(); $new = $this->getNewValue(); $phids[] = $this->getAuthorPHID(); switch ($this->getTransactionType()) { case self::TYPE_DATE_MARKER: break; case self::TYPE_PARTICIPANTS: $phids = array_merge($phids, $this->getOldValue()); $phids = array_merge($phids, $this->getNewValue()); break; } return $phids; } }