2011-01-25 15:19:06 -08:00
|
|
|
<?php
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Copyright 2011 Facebook, Inc.
|
|
|
|
|
*
|
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
|
*
|
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
*
|
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
|
* limitations under the License.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
class DifferentialRevisionListController extends DifferentialController {
|
|
|
|
|
|
2011-01-27 11:35:04 -08:00
|
|
|
private $filter;
|
|
|
|
|
|
|
|
|
|
public function willProcessRequest(array $data) {
|
|
|
|
|
$this->filter = idx($data, 'filter');
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-25 15:19:06 -08:00
|
|
|
public function processRequest() {
|
2011-04-11 02:03:30 -07:00
|
|
|
$request = $this->getRequest();
|
|
|
|
|
$user = $request->getUser();
|
|
|
|
|
|
|
|
|
|
if ($request->isFormPost()) {
|
|
|
|
|
$phid_arr = $request->getArr('view_user');
|
|
|
|
|
$view_target = head($phid_arr);
|
|
|
|
|
return id(new AphrontRedirectResponse())
|
|
|
|
|
->setURI($request->getRequestURI()->alter('phid', $view_target));
|
|
|
|
|
}
|
2011-01-25 15:19:06 -08:00
|
|
|
|
2011-01-27 11:35:04 -08:00
|
|
|
$filters = array(
|
2011-04-11 02:03:30 -07:00
|
|
|
'User Revisions',
|
2011-01-27 11:35:04 -08:00
|
|
|
'active' => array(
|
|
|
|
|
'name' => 'Active Revisions',
|
|
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query'
|
|
|
|
|
=> DifferentialRevisionListData::QUERY_NEED_ACTION_FROM_SELF,
|
|
|
|
|
'header' => 'Action Required',
|
|
|
|
|
'nodata' => 'You have no revisions requiring action.',
|
|
|
|
|
),
|
|
|
|
|
array(
|
|
|
|
|
'query'
|
|
|
|
|
=> DifferentialRevisionListData::QUERY_NEED_ACTION_FROM_OTHERS,
|
|
|
|
|
'header' => 'Waiting on Others',
|
|
|
|
|
'nodata' => 'You have no revisions waiting on others',
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
'open' => array(
|
|
|
|
|
'name' => 'Open Revisions',
|
|
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query' => DifferentialRevisionListData::QUERY_OPEN_OWNED,
|
2011-02-22 23:37:43 -08:00
|
|
|
'header' => 'Your Open Revisions',
|
2011-01-27 11:35:04 -08:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
'reviews' => array(
|
|
|
|
|
'name' => 'Open Reviews',
|
|
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query' => DifferentialRevisionListData::QUERY_OPEN_REVIEWER,
|
2011-02-22 23:37:43 -08:00
|
|
|
'header' => 'Your Open Reviews',
|
2011-01-27 11:35:04 -08:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
'all' => array(
|
|
|
|
|
'name' => 'All Revisions',
|
|
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query' => DifferentialRevisionListData::QUERY_OWNED,
|
2011-02-22 23:37:43 -08:00
|
|
|
'header' => 'Your Revisions',
|
2011-01-27 11:35:04 -08:00
|
|
|
),
|
2011-01-25 15:19:06 -08:00
|
|
|
),
|
2011-01-27 11:35:04 -08:00
|
|
|
),
|
|
|
|
|
'related' => array(
|
|
|
|
|
'name' => 'All Revisions and Reviews',
|
|
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query' => DifferentialRevisionListData::QUERY_OWNED_OR_REVIEWER,
|
2011-02-22 23:37:43 -08:00
|
|
|
'header' => 'Your Revisions and Reviews',
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
Differential Updates View
Summary:
This adds a new view to differential called Updates.
The high-level goal of Updates is to enabled differential to be
effectively used without email notifications. I've tried doing things
like automatically deleting differential emails where I'm in the 'to'
line since they show up on the main diffential page but then there's
always the chance an important diff flies by without me seeing it. Also,
sometimes someone comments on a diff post-commit but differential
doesn't surface those diffs.
I re-created a test db on my devserver using mysqldump to get data on
revs > 230000 so I would have some test data. We need to add a simple
viewtime table but I didn't want to do that in production. Here's the
table:
CREATE TABLE differential_viewtime (
viewerPHID varchar(64) not null,
objectPHID varchar(64) not null,
viewTime int unsigned not null,
PRIMARY KEY (viewerPHID, objectPHID)
);
Issues:
-Once we turn this on, all diffs will be 'unviewed'. What do you think
about a 'Clear All' button or something?
-Maybe we should add a pager
This feature would be insanely useful, let me know what you think.
Test Plan:
Loaded Updates in my sandbox
http://phabricator.dev1577.snc6.facebook.com/differential/filter/updates/
Clicked a diff, then went back, made sure diff disappeared from Updates
list
Reviewed By: tuomaspelkonen
Reviewers: epriestley, jungejason, tuomaspelkonen
Commenters: epriestley
CC: epriestley, elynde, tuomaspelkonen
Differential Revision: 169
2011-04-27 01:27:06 -07:00
|
|
|
'updates' => array(
|
|
|
|
|
'name' => 'Updates',
|
|
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query' => DifferentialRevisionListData::QUERY_UPDATED_SINCE,
|
|
|
|
|
'header' =>
|
|
|
|
|
'Diffs that have been updated since you\'ve last viewed them',
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
2011-02-22 23:37:43 -08:00
|
|
|
'<hr />',
|
|
|
|
|
'All Revisions',
|
|
|
|
|
'allopen' => array(
|
|
|
|
|
'name' => 'Open',
|
2011-04-11 02:03:30 -07:00
|
|
|
'nofilter' => true,
|
2011-02-22 23:37:43 -08:00
|
|
|
'queries' => array(
|
|
|
|
|
array(
|
|
|
|
|
'query' => DifferentialRevisionListData::QUERY_ALL_OPEN,
|
|
|
|
|
'header' => 'All Open Revisions',
|
2011-01-27 11:35:04 -08:00
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (empty($filters[$this->filter])) {
|
2011-02-22 23:39:09 -08:00
|
|
|
$this->filter = 'active';
|
2011-01-27 11:35:04 -08:00
|
|
|
}
|
|
|
|
|
|
2011-04-11 02:03:30 -07:00
|
|
|
$view_phid = nonempty($request->getStr('phid'), $user->getPHID());
|
2011-01-27 11:35:04 -08:00
|
|
|
|
|
|
|
|
$queries = array();
|
|
|
|
|
$filter = $filters[$this->filter];
|
|
|
|
|
foreach ($filter['queries'] as $query) {
|
|
|
|
|
$query_object = new DifferentialRevisionListData(
|
|
|
|
|
$query['query'],
|
2011-04-11 02:03:30 -07:00
|
|
|
array($view_phid));
|
2011-01-27 11:35:04 -08:00
|
|
|
$queries[] = array(
|
|
|
|
|
'object' => $query_object,
|
|
|
|
|
) + $query;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$side_nav = new AphrontSideNavView();
|
2011-02-22 23:37:43 -08:00
|
|
|
|
2011-04-11 02:03:30 -07:00
|
|
|
$query = null;
|
|
|
|
|
if ($view_phid) {
|
|
|
|
|
$query = '?phid='.$view_phid;
|
|
|
|
|
}
|
2011-02-22 23:37:43 -08:00
|
|
|
|
2011-01-27 11:35:04 -08:00
|
|
|
foreach ($filters as $filter_name => $filter_desc) {
|
2011-02-22 23:37:43 -08:00
|
|
|
if (is_int($filter_name)) {
|
|
|
|
|
$side_nav->addNavItem(
|
|
|
|
|
phutil_render_tag(
|
|
|
|
|
'span',
|
|
|
|
|
array(),
|
|
|
|
|
$filter_desc));
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2011-01-27 11:35:04 -08:00
|
|
|
$selected = ($filter_name == $this->filter);
|
|
|
|
|
$side_nav->addNavItem(
|
|
|
|
|
phutil_render_tag(
|
|
|
|
|
'a',
|
|
|
|
|
array(
|
2011-04-11 02:03:30 -07:00
|
|
|
'href' => '/differential/filter/'.$filter_name.'/'.$query,
|
2011-01-27 11:35:04 -08:00
|
|
|
'class' => $selected ? 'aphront-side-nav-selected' : null,
|
|
|
|
|
),
|
|
|
|
|
phutil_escape_html($filter_desc['name'])));
|
|
|
|
|
}
|
2011-01-25 15:19:06 -08:00
|
|
|
|
2011-01-30 18:24:57 -08:00
|
|
|
$phids = array();
|
2011-04-11 02:03:30 -07:00
|
|
|
|
|
|
|
|
$phids[$view_phid] = true;
|
|
|
|
|
|
2011-01-30 18:24:57 -08:00
|
|
|
$rev_ids = array();
|
|
|
|
|
foreach ($queries as $key => $query) {
|
|
|
|
|
$revisions = $query['object']->loadRevisions();
|
|
|
|
|
foreach ($revisions as $revision) {
|
|
|
|
|
$phids[$revision->getAuthorPHID()] = true;
|
|
|
|
|
$rev_ids[$revision->getID()] = true;
|
|
|
|
|
}
|
|
|
|
|
$queries[$key]['revisions'] = $revisions;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$rev = new DifferentialRevision();
|
|
|
|
|
if ($rev_ids) {
|
|
|
|
|
$rev_ids = array_keys($rev_ids);
|
|
|
|
|
$reviewers = queryfx_all(
|
|
|
|
|
$rev->establishConnection('r'),
|
|
|
|
|
'SELECT revisionID, objectPHID FROM %T revision JOIN %T relationship
|
|
|
|
|
ON revision.id = relationship.revisionID
|
|
|
|
|
WHERE revision.id IN (%Ld)
|
|
|
|
|
AND relationship.relation = %s
|
|
|
|
|
ORDER BY sequence',
|
|
|
|
|
$rev->getTableName(),
|
|
|
|
|
DifferentialRevision::RELATIONSHIP_TABLE,
|
|
|
|
|
$rev_ids,
|
|
|
|
|
DifferentialRevision::RELATION_REVIEWER);
|
|
|
|
|
|
|
|
|
|
$reviewer_map = array();
|
|
|
|
|
foreach ($reviewers as $reviewer) {
|
|
|
|
|
$reviewer_map[$reviewer['revisionID']][] = $reviewer['objectPHID'];
|
|
|
|
|
}
|
|
|
|
|
foreach ($reviewer_map as $revision_id => $reviewer_ids) {
|
|
|
|
|
$phids[reset($reviewer_ids)] = true;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$reviewer_map = array();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($phids) {
|
|
|
|
|
$phids = array_keys($phids);
|
|
|
|
|
$handles = id(new PhabricatorObjectHandleData($phids))
|
|
|
|
|
->loadHandles();
|
|
|
|
|
} else {
|
|
|
|
|
$handles = array();
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-11 02:03:30 -07:00
|
|
|
if (empty($filters[$this->filter]['nofilter'])) {
|
|
|
|
|
$filter_form = id(new AphrontFormView())
|
|
|
|
|
->setUser($user)
|
|
|
|
|
->appendChild(
|
|
|
|
|
id(new AphrontFormTokenizerControl())
|
|
|
|
|
->setDatasource('/typeahead/common/users/')
|
|
|
|
|
->setLabel('View User')
|
|
|
|
|
->setName('view_user')
|
|
|
|
|
->setValue(
|
|
|
|
|
array(
|
|
|
|
|
$view_phid => $handles[$view_phid]->getFullName(),
|
|
|
|
|
))
|
|
|
|
|
->setLimit(1));
|
|
|
|
|
|
|
|
|
|
$filter_view = new AphrontListFilterView();
|
|
|
|
|
$filter_view->appendChild($filter_form);
|
|
|
|
|
$side_nav->appendChild($filter_view);
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-27 11:35:04 -08:00
|
|
|
foreach ($queries as $query) {
|
|
|
|
|
$table = $this->renderRevisionTable(
|
2011-01-30 18:24:57 -08:00
|
|
|
$query['revisions'],
|
2011-01-27 11:35:04 -08:00
|
|
|
$query['header'],
|
2011-01-30 18:24:57 -08:00
|
|
|
idx($query, 'nodata'),
|
|
|
|
|
$handles,
|
|
|
|
|
$reviewer_map);
|
2011-01-27 11:35:04 -08:00
|
|
|
$side_nav->appendChild($table);
|
|
|
|
|
}
|
2011-01-25 15:19:06 -08:00
|
|
|
|
|
|
|
|
return $this->buildStandardPageResponse(
|
|
|
|
|
$side_nav,
|
|
|
|
|
array(
|
|
|
|
|
'title' => 'Differential Home',
|
2011-02-05 12:20:18 -08:00
|
|
|
'tab' => 'revisions',
|
2011-01-25 15:19:06 -08:00
|
|
|
));
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-30 18:24:57 -08:00
|
|
|
private function renderRevisionTable(
|
|
|
|
|
array $revisions,
|
|
|
|
|
$header,
|
|
|
|
|
$nodata,
|
|
|
|
|
array $handles,
|
|
|
|
|
array $reviewer_map) {
|
2011-01-27 11:35:04 -08:00
|
|
|
|
|
|
|
|
$rows = array();
|
|
|
|
|
foreach ($revisions as $revision) {
|
|
|
|
|
$status = DifferentialRevisionStatus::getNameForRevisionStatus(
|
|
|
|
|
$revision->getStatus());
|
|
|
|
|
|
2011-01-30 18:24:57 -08:00
|
|
|
$reviewers = idx($reviewer_map, $revision->getID(), array());
|
|
|
|
|
if ($reviewers) {
|
|
|
|
|
$first = reset($reviewers);
|
|
|
|
|
if (count($reviewers) > 1) {
|
|
|
|
|
$suffix = ' (+'.(count($reviewers) - 1).')';
|
|
|
|
|
} else {
|
|
|
|
|
$suffix = null;
|
|
|
|
|
}
|
|
|
|
|
$reviewers = $handles[$first]->renderLink().$suffix;
|
|
|
|
|
} else {
|
|
|
|
|
$reviewers = '<em>None</em>';
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-27 11:35:04 -08:00
|
|
|
$rows[] = array(
|
|
|
|
|
'D'.$revision->getID(),
|
2011-01-30 18:24:57 -08:00
|
|
|
'<strong>'.phutil_render_tag(
|
2011-01-27 11:35:04 -08:00
|
|
|
'a',
|
|
|
|
|
array(
|
|
|
|
|
'href' => '/D'.$revision->getID(),
|
|
|
|
|
),
|
2011-01-30 18:24:57 -08:00
|
|
|
phutil_escape_html($revision->getTitle())).'</strong>',
|
2011-01-27 11:35:04 -08:00
|
|
|
phutil_escape_html($status),
|
|
|
|
|
number_format($revision->getLineCount()),
|
2011-01-30 18:24:57 -08:00
|
|
|
$handles[$revision->getAuthorPHID()]->renderLink(),
|
|
|
|
|
$reviewers,
|
|
|
|
|
phabricator_format_timestamp($revision->getDateModified()),
|
|
|
|
|
phabricator_format_timestamp($revision->getDateCreated()),
|
2011-01-27 11:35:04 -08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$table = new AphrontTableView($rows);
|
|
|
|
|
$table->setHeaders(
|
|
|
|
|
array(
|
|
|
|
|
'ID',
|
|
|
|
|
'Revision',
|
|
|
|
|
'Status',
|
|
|
|
|
'Lines',
|
|
|
|
|
'Author',
|
|
|
|
|
'Reviewers',
|
|
|
|
|
'Updated',
|
|
|
|
|
'Created',
|
|
|
|
|
));
|
|
|
|
|
$table->setColumnClasses(
|
|
|
|
|
array(
|
|
|
|
|
null,
|
|
|
|
|
'wide',
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
null,
|
|
|
|
|
));
|
|
|
|
|
if ($nodata !== null) {
|
|
|
|
|
$table->setNoDataString($nodata);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
$panel = new AphrontPanelView();
|
|
|
|
|
$panel->setHeader($header);
|
|
|
|
|
$panel->appendChild($table);
|
|
|
|
|
|
|
|
|
|
return $panel;
|
|
|
|
|
}
|
|
|
|
|
|
2011-01-25 15:19:06 -08:00
|
|
|
}
|