Page Menu
Home
c4science
Search
Configure Global Search
Log In
Files
F93452884
DifferentialRevisionQuery.php
No One
Temporary
Actions
Download File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Subscribers
None
File Metadata
Details
File Info
Storage
Attached
Created
Thu, Nov 28, 21:22
Size
26 KB
Mime Type
text/x-php
Expires
Sat, Nov 30, 21:22 (2 d)
Engine
blob
Format
Raw Data
Handle
22642032
Attached To
rPH Phabricator
DifferentialRevisionQuery.php
View Options
<?php
/**
* @task config Query Configuration
* @task exec Query Execution
* @task internal Internals
*/
final
class
DifferentialRevisionQuery
extends
PhabricatorCursorPagedPolicyAwareQuery
{
private
$pathIDs
=
array
();
private
$authors
=
array
();
private
$draftAuthors
=
array
();
private
$ccs
=
array
();
private
$reviewers
=
array
();
private
$revIDs
=
array
();
private
$commitHashes
=
array
();
private
$commitPHIDs
=
array
();
private
$phids
=
array
();
private
$responsibles
=
array
();
private
$branches
=
array
();
private
$repositoryPHIDs
;
private
$updatedEpochMin
;
private
$updatedEpochMax
;
private
$statuses
;
private
$isOpen
;
const
ORDER_MODIFIED
=
'order-modified'
;
const
ORDER_CREATED
=
'order-created'
;
private
$needActiveDiffs
=
false
;
private
$needDiffIDs
=
false
;
private
$needCommitPHIDs
=
false
;
private
$needHashes
=
false
;
private
$needReviewers
=
false
;
private
$needReviewerAuthority
;
private
$needDrafts
;
private
$needFlags
;
private
$buildingGlobalOrder
;
/* -( Query Configuration )------------------------------------------------ */
/**
* Filter results to revisions which affect a Diffusion path ID in a given
* repository. You can call this multiple times to select revisions for
* several paths.
*
* @param int Diffusion repository ID.
* @param int Diffusion path ID.
* @return this
* @task config
*/
public
function
withPath
(
$repository_id
,
$path_id
)
{
$this
->
pathIDs
[]
=
array
(
'repositoryID'
=>
$repository_id
,
'pathID'
=>
$path_id
,
);
return
$this
;
}
/**
* Filter results to revisions authored by one of the given PHIDs. Calling
* this function will clear anything set by previous calls to
* @{method:withAuthors}.
*
* @param array List of PHIDs of authors
* @return this
* @task config
*/
public
function
withAuthors
(
array
$author_phids
)
{
$this
->
authors
=
$author_phids
;
return
$this
;
}
/**
* Filter results to revisions which CC one of the listed people. Calling this
* function will clear anything set by previous calls to @{method:withCCs}.
*
* @param array List of PHIDs of subscribers.
* @return this
* @task config
*/
public
function
withCCs
(
array
$cc_phids
)
{
$this
->
ccs
=
$cc_phids
;
return
$this
;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* reviewers. Calling this function will clear anything set by previous calls
* to @{method:withReviewers}.
*
* @param array List of PHIDs of reviewers
* @return this
* @task config
*/
public
function
withReviewers
(
array
$reviewer_phids
)
{
$this
->
reviewers
=
$reviewer_phids
;
return
$this
;
}
/**
* Filter results to revisions that have one of the provided commit hashes.
* Calling this function will clear anything set by previous calls to
* @{method:withCommitHashes}.
*
* @param array List of pairs <Class
* ArcanistDifferentialRevisionHash::HASH_$type constant,
* hash>
* @return this
* @task config
*/
public
function
withCommitHashes
(
array
$commit_hashes
)
{
$this
->
commitHashes
=
$commit_hashes
;
return
$this
;
}
/**
* Filter results to revisions that have one of the provided PHIDs as
* commits. Calling this function will clear anything set by previous calls
* to @{method:withCommitPHIDs}.
*
* @param array List of PHIDs of commits
* @return this
* @task config
*/
public
function
withCommitPHIDs
(
array
$commit_phids
)
{
$this
->
commitPHIDs
=
$commit_phids
;
return
$this
;
}
public
function
withStatuses
(
array
$statuses
)
{
$this
->
statuses
=
$statuses
;
return
$this
;
}
public
function
withIsOpen
(
$is_open
)
{
$this
->
isOpen
=
$is_open
;
return
$this
;
}
/**
* Filter results to revisions on given branches.
*
* @param list List of branch names.
* @return this
* @task config
*/
public
function
withBranches
(
array
$branches
)
{
$this
->
branches
=
$branches
;
return
$this
;
}
/**
* Filter results to only return revisions whose ids are in the given set.
*
* @param array List of revision ids
* @return this
* @task config
*/
public
function
withIDs
(
array
$ids
)
{
$this
->
revIDs
=
$ids
;
return
$this
;
}
/**
* Filter results to only return revisions whose PHIDs are in the given set.
*
* @param array List of revision PHIDs
* @return this
* @task config
*/
public
function
withPHIDs
(
array
$phids
)
{
$this
->
phids
=
$phids
;
return
$this
;
}
/**
* Given a set of users, filter results to return only revisions they are
* responsible for (i.e., they are either authors or reviewers).
*
* @param array List of user PHIDs.
* @return this
* @task config
*/
public
function
withResponsibleUsers
(
array
$responsible_phids
)
{
$this
->
responsibles
=
$responsible_phids
;
return
$this
;
}
public
function
withRepositoryPHIDs
(
array
$repository_phids
)
{
$this
->
repositoryPHIDs
=
$repository_phids
;
return
$this
;
}
public
function
withUpdatedEpochBetween
(
$min
,
$max
)
{
$this
->
updatedEpochMin
=
$min
;
$this
->
updatedEpochMax
=
$max
;
return
$this
;
}
/**
* Set whether or not the query should load the active diff for each
* revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public
function
needActiveDiffs
(
$need_active_diffs
)
{
$this
->
needActiveDiffs
=
$need_active_diffs
;
return
$this
;
}
/**
* Set whether or not the query should load the associated commit PHIDs for
* each revision.
*
* @param bool True to load and attach diffs.
* @return this
* @task config
*/
public
function
needCommitPHIDs
(
$need_commit_phids
)
{
$this
->
needCommitPHIDs
=
$need_commit_phids
;
return
$this
;
}
/**
* Set whether or not the query should load associated diff IDs for each
* revision.
*
* @param bool True to load and attach diff IDs.
* @return this
* @task config
*/
public
function
needDiffIDs
(
$need_diff_ids
)
{
$this
->
needDiffIDs
=
$need_diff_ids
;
return
$this
;
}
/**
* Set whether or not the query should load associated commit hashes for each
* revision.
*
* @param bool True to load and attach commit hashes.
* @return this
* @task config
*/
public
function
needHashes
(
$need_hashes
)
{
$this
->
needHashes
=
$need_hashes
;
return
$this
;
}
/**
* Set whether or not the query should load associated reviewers.
*
* @param bool True to load and attach reviewers.
* @return this
* @task config
*/
public
function
needReviewers
(
$need_reviewers
)
{
$this
->
needReviewers
=
$need_reviewers
;
return
$this
;
}
/**
* Request information about the viewer's authority to act on behalf of each
* reviewer. In particular, they have authority to act on behalf of projects
* they are a member of.
*
* @param bool True to load and attach authority.
* @return this
* @task config
*/
public
function
needReviewerAuthority
(
$need_reviewer_authority
)
{
$this
->
needReviewerAuthority
=
$need_reviewer_authority
;
return
$this
;
}
public
function
needFlags
(
$need_flags
)
{
$this
->
needFlags
=
$need_flags
;
return
$this
;
}
public
function
needDrafts
(
$need_drafts
)
{
$this
->
needDrafts
=
$need_drafts
;
return
$this
;
}
/* -( Query Execution )---------------------------------------------------- */
public
function
newResultObject
()
{
return
new
DifferentialRevision
();
}
/**
* Execute the query as configured, returning matching
* @{class:DifferentialRevision} objects.
*
* @return list List of matching DifferentialRevision objects.
* @task exec
*/
protected
function
loadPage
()
{
$data
=
$this
->
loadData
();
$table
=
$this
->
newResultObject
();
return
$table
->
loadAllFromArray
(
$data
);
}
protected
function
willFilterPage
(
array
$revisions
)
{
$viewer
=
$this
->
getViewer
();
$repository_phids
=
mpull
(
$revisions
,
'getRepositoryPHID'
);
$repository_phids
=
array_filter
(
$repository_phids
);
$repositories
=
array
();
if
(
$repository_phids
)
{
$repositories
=
id
(
new
PhabricatorRepositoryQuery
())
->
setViewer
(
$this
->
getViewer
())
->
withPHIDs
(
$repository_phids
)
->
execute
();
$repositories
=
mpull
(
$repositories
,
null
,
'getPHID'
);
}
// If a revision is associated with a repository:
//
// - the viewer must be able to see the repository; or
// - the viewer must have an automatic view capability.
//
// In the latter case, we'll load the revision but not load the repository.
$can_view
=
PhabricatorPolicyCapability
::
CAN_VIEW
;
foreach
(
$revisions
as
$key
=>
$revision
)
{
$repo_phid
=
$revision
->
getRepositoryPHID
();
if
(!
$repo_phid
)
{
// The revision has no associated repository. Attach `null` and move on.
$revision
->
attachRepository
(
null
);
continue
;
}
$repository
=
idx
(
$repositories
,
$repo_phid
);
if
(
$repository
)
{
// The revision has an associated repository, and the viewer can see
// it. Attach it and move on.
$revision
->
attachRepository
(
$repository
);
continue
;
}
if
(
$revision
->
hasAutomaticCapability
(
$can_view
,
$viewer
))
{
// The revision has an associated repository which the viewer can not
// see, but the viewer has an automatic capability on this revision.
// Load the revision without attaching a repository.
$revision
->
attachRepository
(
null
);
continue
;
}
if
(
$this
->
getViewer
()->
isOmnipotent
())
{
// The viewer is omnipotent. Allow the revision to load even without
// a repository.
$revision
->
attachRepository
(
null
);
continue
;
}
// The revision has an associated repository, and the viewer can't see
// it, and the viewer has no special capabilities. Filter out this
// revision.
$this
->
didRejectResult
(
$revision
);
unset
(
$revisions
[
$key
]);
}
if
(!
$revisions
)
{
return
array
();
}
$table
=
new
DifferentialRevision
();
$conn_r
=
$table
->
establishConnection
(
'r'
);
if
(
$this
->
needCommitPHIDs
)
{
$this
->
loadCommitPHIDs
(
$conn_r
,
$revisions
);
}
$need_active
=
$this
->
needActiveDiffs
;
$need_ids
=
$need_active
||
$this
->
needDiffIDs
;
if
(
$need_ids
)
{
$this
->
loadDiffIDs
(
$conn_r
,
$revisions
);
}
if
(
$need_active
)
{
$this
->
loadActiveDiffs
(
$conn_r
,
$revisions
);
}
if
(
$this
->
needHashes
)
{
$this
->
loadHashes
(
$conn_r
,
$revisions
);
}
if
(
$this
->
needReviewers
||
$this
->
needReviewerAuthority
)
{
$this
->
loadReviewers
(
$conn_r
,
$revisions
);
}
return
$revisions
;
}
protected
function
didFilterPage
(
array
$revisions
)
{
$viewer
=
$this
->
getViewer
();
if
(
$this
->
needFlags
)
{
$flags
=
id
(
new
PhabricatorFlagQuery
())
->
setViewer
(
$viewer
)
->
withOwnerPHIDs
(
array
(
$viewer
->
getPHID
()))
->
withObjectPHIDs
(
mpull
(
$revisions
,
'getPHID'
))
->
execute
();
$flags
=
mpull
(
$flags
,
null
,
'getObjectPHID'
);
foreach
(
$revisions
as
$revision
)
{
$revision
->
attachFlag
(
$viewer
,
idx
(
$flags
,
$revision
->
getPHID
()));
}
}
if
(
$this
->
needDrafts
)
{
PhabricatorDraftEngine
::
attachDrafts
(
$viewer
,
$revisions
);
}
return
$revisions
;
}
private
function
loadData
()
{
$table
=
$this
->
newResultObject
();
$conn_r
=
$table
->
establishConnection
(
'r'
);
$selects
=
array
();
// NOTE: If the query includes "responsiblePHIDs", we execute it as a
// UNION of revisions they own and revisions they're reviewing. This has
// much better performance than doing it with JOIN/WHERE.
if
(
$this
->
responsibles
)
{
$basic_authors
=
$this
->
authors
;
$basic_reviewers
=
$this
->
reviewers
;
try
{
// Build the query where the responsible users are authors.
$this
->
authors
=
array_merge
(
$basic_authors
,
$this
->
responsibles
);
$this
->
reviewers
=
$basic_reviewers
;
$selects
[]
=
$this
->
buildSelectStatement
(
$conn_r
);
// Build the query where the responsible users are reviewers, or
// projects they are members of are reviewers.
$this
->
authors
=
$basic_authors
;
$this
->
reviewers
=
array_merge
(
$basic_reviewers
,
$this
->
responsibles
);
$selects
[]
=
$this
->
buildSelectStatement
(
$conn_r
);
// Put everything back like it was.
$this
->
authors
=
$basic_authors
;
$this
->
reviewers
=
$basic_reviewers
;
}
catch
(
Exception
$ex
)
{
$this
->
authors
=
$basic_authors
;
$this
->
reviewers
=
$basic_reviewers
;
throw
$ex
;
}
}
else
{
$selects
[]
=
$this
->
buildSelectStatement
(
$conn_r
);
}
if
(
count
(
$selects
)
>
1
)
{
$this
->
buildingGlobalOrder
=
true
;
$query
=
qsprintf
(
$conn_r
,
'%Q %Q %Q'
,
implode
(
' UNION DISTINCT '
,
$selects
),
$this
->
buildOrderClause
(
$conn_r
),
$this
->
buildLimitClause
(
$conn_r
));
}
else
{
$query
=
head
(
$selects
);
}
return
queryfx_all
(
$conn_r
,
'%Q'
,
$query
);
}
private
function
buildSelectStatement
(
AphrontDatabaseConnection
$conn_r
)
{
$table
=
new
DifferentialRevision
();
$select
=
$this
->
buildSelectClause
(
$conn_r
);
$from
=
qsprintf
(
$conn_r
,
'FROM %T r'
,
$table
->
getTableName
());
$joins
=
$this
->
buildJoinsClause
(
$conn_r
);
$where
=
$this
->
buildWhereClause
(
$conn_r
);
$group_by
=
$this
->
buildGroupClause
(
$conn_r
);
$having
=
$this
->
buildHavingClause
(
$conn_r
);
$this
->
buildingGlobalOrder
=
false
;
$order_by
=
$this
->
buildOrderClause
(
$conn_r
);
$limit
=
$this
->
buildLimitClause
(
$conn_r
);
return
qsprintf
(
$conn_r
,
'(%Q %Q %Q %Q %Q %Q %Q %Q)'
,
$select
,
$from
,
$joins
,
$where
,
$group_by
,
$having
,
$order_by
,
$limit
);
}
/* -( Internals )---------------------------------------------------------- */
/**
* @task internal
*/
private
function
buildJoinsClause
(
$conn_r
)
{
$joins
=
array
();
if
(
$this
->
pathIDs
)
{
$path_table
=
new
DifferentialAffectedPath
();
$joins
[]
=
qsprintf
(
$conn_r
,
'JOIN %T p ON p.revisionID = r.id'
,
$path_table
->
getTableName
());
}
if
(
$this
->
commitHashes
)
{
$joins
[]
=
qsprintf
(
$conn_r
,
'JOIN %T hash_rel ON hash_rel.revisionID = r.id'
,
ArcanistDifferentialRevisionHash
::
TABLE_NAME
);
}
if
(
$this
->
ccs
)
{
$joins
[]
=
qsprintf
(
$conn_r
,
'JOIN %T e_ccs ON e_ccs.src = r.phid '
.
'AND e_ccs.type = %s '
.
'AND e_ccs.dst in (%Ls)'
,
PhabricatorEdgeConfig
::
TABLE_NAME_EDGE
,
PhabricatorObjectHasSubscriberEdgeType
::
EDGECONST
,
$this
->
ccs
);
}
if
(
$this
->
reviewers
)
{
$joins
[]
=
qsprintf
(
$conn_r
,
'JOIN %T reviewer ON reviewer.revisionPHID = r.phid
AND reviewer.reviewerStatus != %s
AND reviewer.reviewerPHID in (%Ls)'
,
id
(
new
DifferentialReviewer
())->
getTableName
(),
DifferentialReviewerStatus
::
STATUS_RESIGNED
,
$this
->
reviewers
);
}
if
(
$this
->
draftAuthors
)
{
$joins
[]
=
qsprintf
(
$conn_r
,
'JOIN %T has_draft ON has_draft.srcPHID = r.phid
AND has_draft.type = %s
AND has_draft.dstPHID IN (%Ls)'
,
PhabricatorEdgeConfig
::
TABLE_NAME_EDGE
,
PhabricatorObjectHasDraftEdgeType
::
EDGECONST
,
$this
->
draftAuthors
);
}
if
(
$this
->
commitPHIDs
)
{
$joins
[]
=
qsprintf
(
$conn_r
,
'JOIN %T commits ON commits.revisionID = r.id'
,
DifferentialRevision
::
TABLE_COMMIT
);
}
$joins
[]
=
$this
->
buildJoinClauseParts
(
$conn_r
);
return
$this
->
formatJoinClause
(
$joins
);
}
/**
* @task internal
*/
protected
function
buildWhereClause
(
AphrontDatabaseConnection
$conn_r
)
{
$where
=
array
();
if
(
$this
->
pathIDs
)
{
$path_clauses
=
array
();
$repo_info
=
igroup
(
$this
->
pathIDs
,
'repositoryID'
);
foreach
(
$repo_info
as
$repository_id
=>
$paths
)
{
$path_clauses
[]
=
qsprintf
(
$conn_r
,
'(p.repositoryID = %d AND p.pathID IN (%Ld))'
,
$repository_id
,
ipull
(
$paths
,
'pathID'
));
}
$path_clauses
=
'('
.
implode
(
' OR '
,
$path_clauses
).
')'
;
$where
[]
=
$path_clauses
;
}
if
(
$this
->
authors
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.authorPHID IN (%Ls)'
,
$this
->
authors
);
}
if
(
$this
->
revIDs
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.id IN (%Ld)'
,
$this
->
revIDs
);
}
if
(
$this
->
repositoryPHIDs
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.repositoryPHID IN (%Ls)'
,
$this
->
repositoryPHIDs
);
}
if
(
$this
->
commitHashes
)
{
$hash_clauses
=
array
();
foreach
(
$this
->
commitHashes
as
$info
)
{
list
(
$type
,
$hash
)
=
$info
;
$hash_clauses
[]
=
qsprintf
(
$conn_r
,
'(hash_rel.type = %s AND hash_rel.hash = %s)'
,
$type
,
$hash
);
}
$hash_clauses
=
'('
.
implode
(
' OR '
,
$hash_clauses
).
')'
;
$where
[]
=
$hash_clauses
;
}
if
(
$this
->
commitPHIDs
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'commits.commitPHID IN (%Ls)'
,
$this
->
commitPHIDs
);
}
if
(
$this
->
phids
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.phid IN (%Ls)'
,
$this
->
phids
);
}
if
(
$this
->
branches
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.branchName in (%Ls)'
,
$this
->
branches
);
}
if
(
$this
->
updatedEpochMin
!==
null
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.dateModified >= %d'
,
$this
->
updatedEpochMin
);
}
if
(
$this
->
updatedEpochMax
!==
null
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.dateModified <= %d'
,
$this
->
updatedEpochMax
);
}
if
(
$this
->
statuses
!==
null
)
{
$where
[]
=
qsprintf
(
$conn_r
,
'r.status in (%Ls)'
,
$this
->
statuses
);
}
if
(
$this
->
isOpen
!==
null
)
{
if
(
$this
->
isOpen
)
{
$statuses
=
DifferentialLegacyQuery
::
getModernValues
(
DifferentialLegacyQuery
::
STATUS_OPEN
);
}
else
{
$statuses
=
DifferentialLegacyQuery
::
getModernValues
(
DifferentialLegacyQuery
::
STATUS_CLOSED
);
}
$where
[]
=
qsprintf
(
$conn_r
,
'r.status in (%Ls)'
,
$statuses
);
}
$where
[]
=
$this
->
buildWhereClauseParts
(
$conn_r
);
return
$this
->
formatWhereClause
(
$where
);
}
/**
* @task internal
*/
protected
function
shouldGroupQueryResultRows
()
{
$join_triggers
=
array_merge
(
$this
->
pathIDs
,
$this
->
ccs
,
$this
->
reviewers
);
if
(
count
(
$join_triggers
)
>
1
)
{
return
true
;
}
return
parent
::
shouldGroupQueryResultRows
();
}
public
function
getBuiltinOrders
()
{
$orders
=
parent
::
getBuiltinOrders
()
+
array
(
'updated'
=>
array
(
'vector'
=>
array
(
'updated'
,
'id'
),
'name'
=>
pht
(
'Date Updated (Latest First)'
),
'aliases'
=>
array
(
self
::
ORDER_MODIFIED
),
),
'outdated'
=>
array
(
'vector'
=>
array
(
'-updated'
,
'-id'
),
'name'
=>
pht
(
'Date Updated (Oldest First)'
),
),
);
// Alias the "newest" builtin to the historical key for it.
$orders
[
'newest'
][
'aliases'
][]
=
self
::
ORDER_CREATED
;
return
$orders
;
}
protected
function
getDefaultOrderVector
()
{
return
array
(
'updated'
,
'id'
);
}
public
function
getOrderableColumns
()
{
$primary
=
(
$this
->
buildingGlobalOrder
?
null
:
'r'
);
return
array
(
'id'
=>
array
(
'table'
=>
$primary
,
'column'
=>
'id'
,
'type'
=>
'int'
,
'unique'
=>
true
,
),
'updated'
=>
array
(
'table'
=>
$primary
,
'column'
=>
'dateModified'
,
'type'
=>
'int'
,
),
);
}
protected
function
getPagingValueMap
(
$cursor
,
array
$keys
)
{
$revision
=
$this
->
loadCursorObject
(
$cursor
);
return
array
(
'id'
=>
$revision
->
getID
(),
'updated'
=>
$revision
->
getDateModified
(),
);
}
private
function
loadCommitPHIDs
(
$conn_r
,
array
$revisions
)
{
assert_instances_of
(
$revisions
,
'DifferentialRevision'
);
$commit_phids
=
queryfx_all
(
$conn_r
,
'SELECT * FROM %T WHERE revisionID IN (%Ld)'
,
DifferentialRevision
::
TABLE_COMMIT
,
mpull
(
$revisions
,
'getID'
));
$commit_phids
=
igroup
(
$commit_phids
,
'revisionID'
);
foreach
(
$revisions
as
$revision
)
{
$phids
=
idx
(
$commit_phids
,
$revision
->
getID
(),
array
());
$phids
=
ipull
(
$phids
,
'commitPHID'
);
$revision
->
attachCommitPHIDs
(
$phids
);
}
}
private
function
loadDiffIDs
(
$conn_r
,
array
$revisions
)
{
assert_instances_of
(
$revisions
,
'DifferentialRevision'
);
$diff_table
=
new
DifferentialDiff
();
$diff_ids
=
queryfx_all
(
$conn_r
,
'SELECT revisionID, id FROM %T WHERE revisionID IN (%Ld)
ORDER BY id DESC'
,
$diff_table
->
getTableName
(),
mpull
(
$revisions
,
'getID'
));
$diff_ids
=
igroup
(
$diff_ids
,
'revisionID'
);
foreach
(
$revisions
as
$revision
)
{
$ids
=
idx
(
$diff_ids
,
$revision
->
getID
(),
array
());
$ids
=
ipull
(
$ids
,
'id'
);
$revision
->
attachDiffIDs
(
$ids
);
}
}
private
function
loadActiveDiffs
(
$conn_r
,
array
$revisions
)
{
assert_instances_of
(
$revisions
,
'DifferentialRevision'
);
$diff_table
=
new
DifferentialDiff
();
$load_ids
=
array
();
foreach
(
$revisions
as
$revision
)
{
$diffs
=
$revision
->
getDiffIDs
();
if
(
$diffs
)
{
$load_ids
[]
=
max
(
$diffs
);
}
}
$active_diffs
=
array
();
if
(
$load_ids
)
{
$active_diffs
=
$diff_table
->
loadAllWhere
(
'id IN (%Ld)'
,
$load_ids
);
}
$active_diffs
=
mpull
(
$active_diffs
,
null
,
'getRevisionID'
);
foreach
(
$revisions
as
$revision
)
{
$revision
->
attachActiveDiff
(
idx
(
$active_diffs
,
$revision
->
getID
()));
}
}
private
function
loadHashes
(
AphrontDatabaseConnection
$conn_r
,
array
$revisions
)
{
assert_instances_of
(
$revisions
,
'DifferentialRevision'
);
$data
=
queryfx_all
(
$conn_r
,
'SELECT * FROM %T WHERE revisionID IN (%Ld)'
,
'differential_revisionhash'
,
mpull
(
$revisions
,
'getID'
));
$data
=
igroup
(
$data
,
'revisionID'
);
foreach
(
$revisions
as
$revision
)
{
$hashes
=
idx
(
$data
,
$revision
->
getID
(),
array
());
$list
=
array
();
foreach
(
$hashes
as
$hash
)
{
$list
[]
=
array
(
$hash
[
'type'
],
$hash
[
'hash'
]);
}
$revision
->
attachHashes
(
$list
);
}
}
private
function
loadReviewers
(
AphrontDatabaseConnection
$conn
,
array
$revisions
)
{
assert_instances_of
(
$revisions
,
'DifferentialRevision'
);
$reviewer_table
=
new
DifferentialReviewer
();
$reviewer_rows
=
queryfx_all
(
$conn
,
'SELECT * FROM %T WHERE revisionPHID IN (%Ls)
ORDER BY id ASC'
,
$reviewer_table
->
getTableName
(),
mpull
(
$revisions
,
'getPHID'
));
$reviewer_list
=
$reviewer_table
->
loadAllFromArray
(
$reviewer_rows
);
$reviewer_map
=
mgroup
(
$reviewer_list
,
'getRevisionPHID'
);
foreach
(
$reviewer_map
as
$key
=>
$reviewers
)
{
$reviewer_map
[
$key
]
=
mpull
(
$reviewers
,
null
,
'getReviewerPHID'
);
}
$viewer
=
$this
->
getViewer
();
$viewer_phid
=
$viewer
->
getPHID
();
$allow_key
=
'differential.allow-self-accept'
;
$allow_self
=
PhabricatorEnv
::
getEnvConfig
(
$allow_key
);
// Figure out which of these reviewers the viewer has authority to act as.
if
(
$this
->
needReviewerAuthority
&&
$viewer_phid
)
{
$authority
=
$this
->
loadReviewerAuthority
(
$revisions
,
$reviewer_map
,
$allow_self
);
}
foreach
(
$revisions
as
$revision
)
{
$reviewers
=
idx
(
$reviewer_map
,
$revision
->
getPHID
(),
array
());
foreach
(
$reviewers
as
$reviewer_phid
=>
$reviewer
)
{
if
(
$this
->
needReviewerAuthority
)
{
if
(!
$viewer_phid
)
{
// Logged-out users never have authority.
$has_authority
=
false
;
}
else
if
((!
$allow_self
)
&&
(
$revision
->
getAuthorPHID
()
==
$viewer_phid
))
{
// The author can never have authority unless we allow self-accept.
$has_authority
=
false
;
}
else
{
// Otherwise, look up whether the viewer has authority.
$has_authority
=
isset
(
$authority
[
$reviewer_phid
]);
}
$reviewer
->
attachAuthority
(
$viewer
,
$has_authority
);
}
$reviewers
[
$reviewer_phid
]
=
$reviewer
;
}
$revision
->
attachReviewers
(
$reviewers
);
}
}
private
function
loadReviewerAuthority
(
array
$revisions
,
array
$reviewers
,
$allow_self
)
{
$revision_map
=
mpull
(
$revisions
,
null
,
'getPHID'
);
$viewer_phid
=
$this
->
getViewer
()->
getPHID
();
// Find all the project/package reviewers which the user may have authority
// over.
$project_phids
=
array
();
$package_phids
=
array
();
$project_type
=
PhabricatorProjectProjectPHIDType
::
TYPECONST
;
$package_type
=
PhabricatorOwnersPackagePHIDType
::
TYPECONST
;
foreach
(
$reviewers
as
$revision_phid
=>
$reviewer_list
)
{
if
(!
$allow_self
)
{
if
(
$revision_map
[
$revision_phid
]->
getAuthorPHID
()
==
$viewer_phid
)
{
// If self-review isn't permitted, the user will never have
// authority over projects on revisions they authored because you
// can't accept your own revisions, so we don't need to load any
// data about these reviewers.
continue
;
}
}
foreach
(
$reviewer_list
as
$reviewer_phid
=>
$reviewer
)
{
$phid_type
=
phid_get_type
(
$reviewer_phid
);
if
(
$phid_type
==
$project_type
)
{
$project_phids
[]
=
$reviewer_phid
;
}
if
(
$phid_type
==
$package_type
)
{
$package_phids
[]
=
$reviewer_phid
;
}
}
}
// The viewer has authority over themselves.
$user_authority
=
array_fuse
(
array
(
$viewer_phid
));
// And over any projects they are a member of.
$project_authority
=
array
();
if
(
$project_phids
)
{
$project_authority
=
id
(
new
PhabricatorProjectQuery
())
->
setViewer
(
$this
->
getViewer
())
->
withPHIDs
(
$project_phids
)
->
withMemberPHIDs
(
array
(
$viewer_phid
))
->
execute
();
$project_authority
=
mpull
(
$project_authority
,
'getPHID'
);
$project_authority
=
array_fuse
(
$project_authority
);
}
// And over any packages they own.
$package_authority
=
array
();
if
(
$package_phids
)
{
$package_authority
=
id
(
new
PhabricatorOwnersPackageQuery
())
->
setViewer
(
$this
->
getViewer
())
->
withPHIDs
(
$package_phids
)
->
withAuthorityPHIDs
(
array
(
$viewer_phid
))
->
execute
();
$package_authority
=
mpull
(
$package_authority
,
'getPHID'
);
$package_authority
=
array_fuse
(
$package_authority
);
}
return
$user_authority
+
$project_authority
+
$package_authority
;
}
public
function
getQueryApplicationClass
()
{
return
'PhabricatorDifferentialApplication'
;
}
protected
function
getPrimaryTableAlias
()
{
return
'r'
;
}
}
Event Timeline
Log In to Comment