diff --git a/src/__celerity_resource_map__.php b/src/__celerity_resource_map__.php index 60b5739d33..e5be55698e 100644 --- a/src/__celerity_resource_map__.php +++ b/src/__celerity_resource_map__.php @@ -762,7 +762,7 @@ celerity_register_resource_map(array( ), 'conpherence-message-pane-css' => array( - 'uri' => '/res/88b1927c/rsrc/css/application/conpherence/message-pane.css', + 'uri' => '/res/3aa15b9e/rsrc/css/application/conpherence/message-pane.css', 'type' => 'css', 'requires' => array( @@ -1142,7 +1142,7 @@ celerity_register_resource_map(array( ), 'javelin-behavior-conpherence-menu' => array( - 'uri' => '/res/986774a0/rsrc/js/application/conpherence/behavior-menu.js', + 'uri' => '/res/0ad6ab54/rsrc/js/application/conpherence/behavior-menu.js', 'type' => 'js', 'requires' => array( diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index 62bd26a217..99a89c05f5 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -223,6 +223,7 @@ phutil_register_library_map(array( 'ConpherenceTransactionView' => 'applications/conpherence/view/ConpherenceTransactionView.php', 'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php', 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', + 'ConpherenceWidgetController' => 'applications/conpherence/controller/ConpherenceWidgetController.php', 'DarkConsoleController' => 'aphront/console/DarkConsoleController.php', 'DarkConsoleCore' => 'aphront/console/DarkConsoleCore.php', 'DarkConsoleDataController' => 'aphront/console/DarkConsoleDataController.php', @@ -1743,6 +1744,7 @@ phutil_register_library_map(array( 'ConpherenceTransactionView' => 'AphrontView', 'ConpherenceUpdateController' => 'ConpherenceController', 'ConpherenceViewController' => 'ConpherenceController', + 'ConpherenceWidgetController' => 'ConpherenceController', 'DarkConsoleController' => 'PhabricatorController', 'DarkConsoleDataController' => 'PhabricatorController', 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', @@ -2056,6 +2058,8 @@ phutil_register_library_map(array( array( 0 => 'ManiphestDAO', 1 => 'PhabricatorMarkupInterface', + 2 => 'PhabricatorPolicyInterface', + 3 => 'PhabricatorTokenReceiverInterface', ), 'ManiphestTaskAuxiliaryStorage' => 'ManiphestDAO', 'ManiphestTaskDescriptionChangeController' => 'ManiphestController', diff --git a/src/applications/conpherence/application/PhabricatorApplicationConpherence.php b/src/applications/conpherence/application/PhabricatorApplicationConpherence.php index 9f78872453..91938f2a1e 100644 --- a/src/applications/conpherence/application/PhabricatorApplicationConpherence.php +++ b/src/applications/conpherence/application/PhabricatorApplicationConpherence.php @@ -45,6 +45,7 @@ final class PhabricatorApplicationConpherence extends PhabricatorApplication { '' => 'ConpherenceListController', 'new/' => 'ConpherenceNewController', 'view/(?P[1-9]\d*)/' => 'ConpherenceViewController', + 'widget/(?P[1-9]\d*)/' => 'ConpherenceWidgetController', 'update/(?P[1-9]\d*)/' => 'ConpherenceUpdateController', '(?P[1-9]\d*)/' => 'ConpherenceListController', ), diff --git a/src/applications/conpherence/controller/ConpherenceController.php b/src/applications/conpherence/controller/ConpherenceController.php index 67fecfe264..eaf6c85ed6 100644 --- a/src/applications/conpherence/controller/ConpherenceController.php +++ b/src/applications/conpherence/controller/ConpherenceController.php @@ -101,7 +101,6 @@ abstract class ConpherenceController extends PhabricatorController { $read_conpherences = $this->getReadConpherences(); $user = $this->getRequest()->getUser(); - $menu = new PhabricatorMenuView(); $nav = AphrontSideNavFilterView::newFromMenu($menu); $nav->addClass('conpherence-menu'); @@ -114,12 +113,9 @@ abstract class ConpherenceController extends PhabricatorController { ); $nav->addLabel(pht('Unread')); $nav = $this->addConpherencesToNav($unread_conpherences, $nav); - $nav->addLabel(pht('Read')); $nav = $this->addConpherencesToNav($read_conpherences, $nav, true); - $nav->selectFilter($filter); - return $nav; } diff --git a/src/applications/conpherence/controller/ConpherenceViewController.php b/src/applications/conpherence/controller/ConpherenceViewController.php index 65eee271f9..d2de20b3fc 100644 --- a/src/applications/conpherence/controller/ConpherenceViewController.php +++ b/src/applications/conpherence/controller/ConpherenceViewController.php @@ -37,14 +37,9 @@ final class ConpherenceViewController extends if (!$conpherence_id) { return new Aphront404Response(); } - if (!$request->isAjax()) { - return id(new AphrontRedirectResponse()) - ->setURI($this->getApplicationURI($conpherence_id.'/')); - } $conpherence = id(new ConpherenceThreadQuery()) ->setViewer($user) ->withIDs(array($conpherence_id)) - ->needWidgetData(true) ->needHeaderPics(true) ->executeOne(); $this->setConpherence($conpherence); @@ -58,9 +53,7 @@ final class ConpherenceViewController extends $header = $this->renderHeaderPaneContent(); $messages = $this->renderMessagePaneContent(); - $widgets = $this->renderWidgetPaneContent(); - $content = $header + $widgets + $messages; - + $content = $header + $messages; return id(new AphrontAjaxResponse())->setContent($content); } @@ -125,7 +118,8 @@ final class ConpherenceViewController extends $handles = $conpherence->getHandles(); $rendered_transactions = array(); - $transactions = $conpherence->getTransactions(); + + $transactions = $conpherence->getTransactionsFrom(0, 100); $engine = id(new PhabricatorMarkupEngine()) ->setViewer($user); @@ -137,7 +131,6 @@ final class ConpherenceViewController extends } } $engine->process(); - foreach ($transactions as $transaction) { if ($transaction->shouldHide()) { continue; @@ -168,270 +161,21 @@ final class ConpherenceViewController extends ->setValue(pht('Pontificate')) )->render(); + $scrollbutton = javelin_tag( + 'a', + array( + 'href' => '#', + 'mustcapture' => true, + 'sigil' => 'show-older-messages', + 'class' => 'conpherence-show-older-messages', + ), + pht('Show Older Messages')); + return array( - 'messages' => $transactions, + 'messages' => $scrollbutton.$transactions, 'form' => $form ); } - private function renderWidgetPaneContent() { - require_celerity_resource('conpherence-widget-pane-css'); - require_celerity_resource('sprite-conpher-css'); - Javelin::initBehavior( - 'conpherence-widget-pane', - array( - 'widgetRegistery' => array( - 'widgets-files' => 1, - 'widgets-tasks' => 1, - 'widgets-calendar' => 1, - ) - ) - ); - - $conpherence = $this->getConpherence(); - - $widgets = phutil_tag( - 'div', - array( - 'class' => 'widgets-header' - ), - array( - javelin_tag( - 'a', - array( - 'sigil' => 'conpherence-change-widget', - 'meta' => array( - 'widget' => 'widgets-files', - 'toggleClass' => 'conpher_files_on' - ), - 'id' => 'widgets-files-toggle', - 'class' => 'sprite-conpher conpher_files_off first-icon' - ), - '' - ), - javelin_tag( - 'a', - array( - 'sigil' => 'conpherence-change-widget', - 'meta' => array( - 'widget' => 'widgets-tasks', - 'toggleClass' => 'conpher_list_on' - ), - 'id' => 'widgets-tasks-toggle', - 'class' => 'sprite-conpher conpher_list_off conpher_list_on', - ), - '' - ), - javelin_tag( - 'a', - array( - 'sigil' => 'conpherence-change-widget', - 'meta' => array( - 'widget' => 'widgets-calendar', - 'toggleClass' => 'conpher_calendar_on' - ), - 'id' => 'widgets-calendar-toggle', - 'class' => 'sprite-conpher conpher_calendar_off', - ), - '' - ) - ) - ). - phutil_tag( - 'div', - array( - 'class' => 'widgets-body', - 'id' => 'widgets-files', - 'style' => 'display: none;' - ), - $this->renderFilesWidgetPaneContent() - ). - phutil_tag( - 'div', - array( - 'class' => 'widgets-body', - 'id' => 'widgets-tasks', - ), - $this->renderTaskWidgetPaneContent() - ). - phutil_tag( - 'div', - array( - 'class' => 'widgets-body', - 'id' => 'widgets-calendar', - 'style' => 'display: none;' - ), - $this->renderCalendarWidgetPaneContent() - ); - - return array('widgets' => $widgets); - } - - private function renderFilesWidgetPaneContent() { - $conpherence = $this->getConpherence(); - $widget_data = $conpherence->getWidgetData(); - $files = $widget_data['files']; - - $table_data = array(); - foreach ($files as $file) { - $thumb = $file->getThumb60x45URI(); - $table_data[] = array( - phutil_tag( - 'img', - array( - 'src' => $thumb - ), - ''), - $file->getName(), - ); - } - $header = id(new PhabricatorHeaderView()) - ->setHeader(pht('Attached Files')); - $table = id(new AphrontTableView($table_data)) - ->setNoDataString(pht('No files attached to conpherence.')) - ->setHeaders(array('', pht('Name'))) - ->setColumnClasses(array('', 'wide')); - return hsprintf('%s%s', $header->render(), $table->render()); - } - - private function renderTaskWidgetPaneContent() { - $conpherence = $this->getConpherence(); - $widget_data = $conpherence->getWidgetData(); - $tasks = $widget_data['tasks']; - $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); - $handles = $conpherence->getHandles(); - $content = array(); - foreach ($tasks as $owner_phid => $actual_tasks) { - $handle = $handles[$owner_phid]; - $content[] = id(new PhabricatorHeaderView()) - ->setHeader($handle->getName()) - ->render(); - $actual_tasks = msort($actual_tasks, 'getPriority'); - $actual_tasks = array_reverse($actual_tasks); - $data = array(); - foreach ($actual_tasks as $task) { - $data[] = array( - idx($priority_map, $task->getPriority(), pht('???')), - phutil_tag( - 'a', - array( - 'href' => '/T'.$task->getID() - ), - $task->getTitle() - ) - ); - } - $table = id(new AphrontTableView($data)) - ->setNoDataString(pht('No open tasks.')) - ->setHeaders(array(pht('Pri'), pht('Title'))) - ->setColumnClasses(array('', 'wide')); - $content[] = $table->render(); - } - return phutil_implode_html('', $content); - } - - private function renderCalendarWidgetPaneContent() { - $user = $this->getRequest()->getUser(); - - $conpherence = $this->getConpherence(); - $widget_data = $conpherence->getWidgetData(); - $statuses = $widget_data['statuses']; - $handles = $conpherence->getHandles(); - $content = array(); - $timestamps = $this->getCalendarWidgetWeekTimestamps(); - $one_day = 24 * 60 * 60; - foreach ($timestamps as $time => $day) { - // build a header for the new day - $content[] = id(new PhabricatorHeaderView()) - ->setHeader($day->format('l')) - ->render(); - - $day->setTime(0, 0, 0); - $epoch_start = $day->format('U'); - $day->modify('+1 day'); - $epoch_end = $day->format('U'); - - // keep looking through statuses where we last left off - foreach ($statuses as $status) { - if ($status->getDateFrom() >= $epoch_end) { - // This list is sorted, so we can stop looking. - break; - } - if ($status->getDateFrom() < $epoch_end && - $status->getDateTo() > $epoch_start) { - $timespan = $status->getDateTo() - $status->getDateFrom(); - if ($timespan > $one_day) { - $time_str = 'm/d'; - } else { - $time_str = 'h:i A'; - } - $epoch_range = phabricator_format_local_time( - $status->getDateFrom(), - $user, - $time_str - ) . ' - ' . phabricator_format_local_time( - $status->getDateTo(), - $user, - $time_str - ); - - $content[] = phutil_tag( - 'div', - array( - 'class' => 'user-status '.$status->getTextStatus(), - ), - array( - phutil_tag( - 'div', - array( - 'class' => 'epoch-range' - ), - $epoch_range - ), - phutil_tag( - 'div', - array( - 'class' => 'icon', - ), - '' - ), - phutil_tag( - 'div', - array( - 'class' => 'description' - ), - $status->getTerseSummary($user) - ), - phutil_tag( - 'div', - array( - 'class' => 'participant' - ), - $handles[$status->getUserPHID()]->getName() - ) - ) - ); - } - } - } - - return phutil_implode_html('', $content); - } - - private function getCalendarWidgetWeekTimestamps() { - $user = $this->getRequest()->getUser(); - $timezone = new DateTimeZone($user->getTimezoneIdentifier()); - - $timestamps = array(); - for ($day = 0; $day < 7; $day++) { - $timestamps[] = new DateTime( - sprintf('today +%d days', $day), - $timezone - ); - } - - return $timestamps; - } - } diff --git a/src/applications/conpherence/controller/ConpherenceWidgetController.php b/src/applications/conpherence/controller/ConpherenceWidgetController.php new file mode 100644 index 0000000000..8a1c5acbc6 --- /dev/null +++ b/src/applications/conpherence/controller/ConpherenceWidgetController.php @@ -0,0 +1,312 @@ +conpherence = $conpherence; + return $this; + } + public function getConpherence() { + return $this->conpherence; + } + + public function setConpherenceID($conpherence_id) { + $this->conpherenceID = $conpherence_id; + return $this; + } + public function getConpherenceID() { + return $this->conpherenceID; + } + + public function willProcessRequest(array $data) { + $this->setConpherenceID(idx($data, 'id')); + } + + public function processRequest() { + $request = $this->getRequest(); + $user = $request->getUser(); + + $conpherence_id = $this->getConpherenceID(); + if (!$conpherence_id) { + return new Aphront404Response(); + } + $conpherence = id(new ConpherenceThreadQuery()) + ->setViewer($user) + ->withIDs(array($conpherence_id)) + ->needWidgetData(true) + ->executeOne(); + $this->setConpherence($conpherence); + + $widgets = $this->renderWidgetPaneContent(); + $content = $widgets; + return id(new AphrontAjaxResponse())->setContent($content); + } + + private function renderWidgetPaneContent() { + require_celerity_resource('conpherence-widget-pane-css'); + require_celerity_resource('sprite-conpher-css'); + Javelin::initBehavior( + 'conpherence-widget-pane', + array( + 'widgetRegistery' => array( + 'widgets-files' => 1, + 'widgets-tasks' => 1, + 'widgets-calendar' => 1, + ) + ) + ); + + $conpherence = $this->getConpherence(); + + $widgets = phutil_tag( + 'div', + array( + 'class' => 'widgets-header' + ), + array( + javelin_tag( + 'a', + array( + 'sigil' => 'conpherence-change-widget', + 'meta' => array( + 'widget' => 'widgets-files', + 'toggleClass' => 'conpher_files_on' + ), + 'id' => 'widgets-files-toggle', + 'class' => 'sprite-conpher conpher_files_off first-icon' + ), + '' + ), + javelin_tag( + 'a', + array( + 'sigil' => 'conpherence-change-widget', + 'meta' => array( + 'widget' => 'widgets-tasks', + 'toggleClass' => 'conpher_list_on' + ), + 'id' => 'widgets-tasks-toggle', + 'class' => 'sprite-conpher conpher_list_off conpher_list_on', + ), + '' + ), + javelin_tag( + 'a', + array( + 'sigil' => 'conpherence-change-widget', + 'meta' => array( + 'widget' => 'widgets-calendar', + 'toggleClass' => 'conpher_calendar_on' + ), + 'id' => 'widgets-calendar-toggle', + 'class' => 'sprite-conpher conpher_calendar_off', + ), + '' + ) + ) + ). + phutil_tag( + 'div', + array( + 'class' => 'widgets-body', + 'id' => 'widgets-files', + 'style' => 'display: none;' + ), + $this->renderFilesWidgetPaneContent() + ). + phutil_tag( + 'div', + array( + 'class' => 'widgets-body', + 'id' => 'widgets-tasks', + ), + $this->renderTaskWidgetPaneContent() + ). + phutil_tag( + 'div', + array( + 'class' => 'widgets-body', + 'id' => 'widgets-calendar', + 'style' => 'display: none;' + ), + $this->renderCalendarWidgetPaneContent() + ); + + return array('widgets' => $widgets); + } + + private function renderFilesWidgetPaneContent() { + $conpherence = $this->getConpherence(); + $widget_data = $conpherence->getWidgetData(); + $files = $widget_data['files']; + + $table_data = array(); + foreach ($files as $file) { + $thumb = $file->getThumb60x45URI(); + $table_data[] = array( + phutil_tag( + 'img', + array( + 'src' => $thumb + ), + '' + ), + $file->getName() + ); + } + $header = id(new PhabricatorHeaderView()) + ->setHeader(pht('Attached Files')); + $table = id(new AphrontTableView($table_data)) + ->setNoDataString(pht('No files attached to conpherence.')) + ->setHeaders(array('', pht('Name'))) + ->setColumnClasses(array('', 'wide')); + return new PhutilSafeHTML($header->render() . $table->render()); + } + + private function renderTaskWidgetPaneContent() { + $conpherence = $this->getConpherence(); + $widget_data = $conpherence->getWidgetData(); + $tasks = $widget_data['tasks']; + $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); + $handles = $conpherence->getHandles(); + $content = array(); + foreach ($tasks as $owner_phid => $actual_tasks) { + $handle = $handles[$owner_phid]; + $content[] = id(new PhabricatorHeaderView()) + ->setHeader($handle->getName()) + ->render(); + $actual_tasks = msort($actual_tasks, 'getPriority'); + $actual_tasks = array_reverse($actual_tasks); + $data = array(); + foreach ($actual_tasks as $task) { + $data[] = array( + idx($priority_map, $task->getPriority(), pht('???')), + phutil_tag( + 'a', + array( + 'href' => '/T'.$task->getID() + ), + $task->getTitle() + ) + ); + } + $table = id(new AphrontTableView($data)) + ->setNoDataString(pht('No open tasks.')) + ->setHeaders(array(pht('Pri'), pht('Title'))) + ->setColumnClasses(array('', 'wide')); + $content[] = $table->render(); + } + return new PhutilSafeHTML(implode('', $content)); + } + + private function renderCalendarWidgetPaneContent() { + $user = $this->getRequest()->getUser(); + + $conpherence = $this->getConpherence(); + $widget_data = $conpherence->getWidgetData(); + $statuses = $widget_data['statuses']; + $handles = $conpherence->getHandles(); + $content = array(); + $timestamps = $this->getCalendarWidgetWeekTimestamps(); + $one_day = 24 * 60 * 60; + foreach ($timestamps as $time => $day) { + // build a header for the new day + $content[] = id(new PhabricatorHeaderView()) + ->setHeader($day->format('l')) + ->render(); + + $day->setTime(0, 0, 0); + $epoch_start = $day->format('U'); + $day->modify('+1 day'); + $epoch_end = $day->format('U'); + + // keep looking through statuses where we last left off + foreach ($statuses as $status) { + if ($status->getDateFrom() >= $epoch_end) { + // This list is sorted, so we can stop looking. + break; + } + if ($status->getDateFrom() < $epoch_end && + $status->getDateTo() > $epoch_start) { + $timespan = $status->getDateTo() - $status->getDateFrom(); + if ($timespan > $one_day) { + $time_str = 'm/d'; + } else { + $time_str = 'h:i A'; + } + $epoch_range = phabricator_format_local_time( + $status->getDateFrom(), + $user, + $time_str + ) . ' - ' . phabricator_format_local_time( + $status->getDateTo(), + $user, + $time_str + ); + + $content[] = phutil_tag( + 'div', + array( + 'class' => 'user-status '.$status->getTextStatus(), + ), + array( + phutil_tag( + 'div', + array( + 'class' => 'epoch-range' + ), + $epoch_range + ), + phutil_tag( + 'div', + array( + 'class' => 'icon', + ), + '' + ), + phutil_tag( + 'div', + array( + 'class' => 'description' + ), + $status->getTerseSummary($user) + ), + phutil_tag( + 'div', + array( + 'class' => 'participant' + ), + $handles[$status->getUserPHID()]->getName() + ) + ) + ); + } + } + } + + return new PhutilSafeHTML(implode('', $content)); + } + + private function getCalendarWidgetWeekTimestamps() { + $user = $this->getRequest()->getUser(); + $timezone = new DateTimeZone($user->getTimezoneIdentifier()); + + $timestamps = array(); + for ($day = 0; $day < 7; $day++) { + $timestamps[] = new DateTime( + sprintf('today +%d days', $day), + $timezone + ); + } + + return $timestamps; + } + +} diff --git a/src/applications/conpherence/storage/ConpherenceThread.php b/src/applications/conpherence/storage/ConpherenceThread.php index e824ad6207..9ba2c700a3 100644 --- a/src/applications/conpherence/storage/ConpherenceThread.php +++ b/src/applications/conpherence/storage/ConpherenceThread.php @@ -117,6 +117,23 @@ final class ConpherenceThread extends ConpherenceDAO return $this->transactions; } + public function getTransactionsFrom($begin = 0, $amount = null) { + $length = count($this->transactions); + if ($amount === null) { + $amount === $length; + } + if ($this->transactions === null) { + throw new Exception( + 'You must attachTransactions first!' + ); + } + return array_slice( + $this->transactions, + $length - $begin - $amount, + $amount + ); + } + public function attachFilePHIDs(array $file_phids) { $this->filePHIDs = $file_phids; return $this; diff --git a/webroot/rsrc/css/application/conpherence/message-pane.css b/webroot/rsrc/css/application/conpherence/message-pane.css index e714b9f37f..42fbe5f3f8 100644 --- a/webroot/rsrc/css/application/conpherence/message-pane.css +++ b/webroot/rsrc/css/application/conpherence/message-pane.css @@ -10,6 +10,14 @@ height: 100%; margin: 0px 320px 0px 0px; } +.conpherence-show-older-messages { + display: block; + background: #e0e3ec; + margin: 10px; + text-align: center; + padding: 10px; + color: #18559D; +} .conpherence-message-pane .conpherence-messages { position: fixed; diff --git a/webroot/rsrc/js/application/conpherence/behavior-menu.js b/webroot/rsrc/js/application/conpherence/behavior-menu.js index 64fcbd3130..37a984334f 100644 --- a/webroot/rsrc/js/application/conpherence/behavior-menu.js +++ b/webroot/rsrc/js/application/conpherence/behavior-menu.js @@ -10,11 +10,16 @@ JX.behavior('conpherence-menu', function(config) { + function onwidgetresponse(context, response) { + var widgets = JX.$H(response.widgets); + var widgetsRoot = JX.$(config.widgets_pane); + JX.DOM.setContent(widgetsRoot, widgets); + } + function onresponse(context, response) { var header = JX.$H(response.header); var messages = JX.$H(response.messages); var form = JX.$H(response.form); - var widgets = JX.$H(response.widgets); var headerRoot = JX.$(config.header); var messagesRoot = JX.$(config.messages); var formRoot = JX.$(config.form_pane); @@ -24,7 +29,6 @@ JX.behavior('conpherence-menu', function(config) { JX.DOM.setContent(messagesRoot, messages); messagesRoot.scrollTop = messagesRoot.scrollHeight; JX.DOM.setContent(formRoot, form); - JX.DOM.setContent(widgetsRoot, widgets); var conpherences = JX.DOM.scry( menuRoot, @@ -92,12 +96,25 @@ JX.behavior('conpherence-menu', function(config) { var selected = e.getData().selected; var data = JX.Stratcom.getData(selected); - var uri = config.base_uri + 'view/' + data.id + '/'; + var widget_uri = config.base_uri + 'widget/' + data.id + '/'; new JX.Workflow(uri, {}) .setHandler(JX.bind(null, onresponse, selected)) .start(); + new JX.Workflow(widget_uri, {}) + .setHandler(JX.bind(null, onwidgetresponse, selected)) + .start(); } ); + JX.Stratcom.listen('click', 'show-older-messages', function(e) { + e.kill(); + console.log(document.URL); + new JX.Request('/conpherence/view/1/', function(r) { + console.log('100'); + }) +.setData({offset: 100}) // get the next page +.send(); +}); + });