1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-02-05 19:38:27 +01:00

Add Basic Auditing Functionalities

Summary:
add basic auditing functionalities. For the related commits for a
package, we detect the following conditions which might be suspicious to the
owners of the package:

* no revision specified
* revision not found
* author not match
* reviewedby not match
* owners not involved
* commit author not recognized

The owners of the package can change the status of the audit entries by
accepting it or specify concern.

The owner can turn on/off the auditing for a package.

Test Plan:
*  verified that non-owner cannot see the details of the audit and cannot modify
it
*  verified that all the audit reasons can be detected
*  tested dropdown filtering and package search
*  verified really normal change not detected
*  verified accept/concern a commit
*  tested enable/disable a package for auditing
*  verified one audit applies to all <commit, packages> to the packages the
auditor owns
*  verified that re-parsing a commit won't have effect if there exists a
 relationship for <commit, package> already

Reviewers: epriestley, nh

Reviewed By: epriestley

CC: aran, benmathews, btrahan, mpodobnik, prithvi, TomL, epriestley

Differential Revision: 1242
This commit is contained in:
jungejason 2011-12-17 15:52:54 -08:00
parent 125e5b16db
commit c80d1480d5
26 changed files with 868 additions and 22 deletions

View file

@ -0,0 +1,23 @@
ALTER TABLE phabricator_owners.owners_packagecommitrelationship
ADD COLUMN `auditStatus` varchar(64) NOT NULL,
ADD COLUMN `auditReasons` longtext NOT NULL,
DROP KEY `packagePHID`,
ADD KEY `packagePHID` (`packagePHID`, `auditStatus`, `id`);
CREATE DATABASE IF NOT EXISTS phabricator_audit;
CREATE TABLE IF NOT EXISTs phabricator_audit.audit_comment (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`phid` varchar(64) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`targetPHID` varchar(64) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`actorPHID` varchar(64) CHARACTER SET latin1 COLLATE latin1_bin NOT NULL,
`dateCreated` int(10) unsigned NOT NULL,
`dateModified` int(10) unsigned NOT NULL,
`action` varchar(64) NOT NULL,
`content` longtext NOT NULL,
PRIMARY KEY `id` (`id`),
KEY `targetPHID` (`targetPHID`, `actorPHID`, `id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
ALTER TABLE phabricator_owners.owners_package
ADD COLUMN `auditingEnabled` tinyint(1) NOT NULL DEFAULT 0;

View file

@ -371,6 +371,12 @@ phutil_register_library_map(array(
'ManiphestTransactionType' => 'applications/maniphest/constants/transactiontype',
'ManiphestView' => 'applications/maniphest/view/base',
'Phabricator404Controller' => 'applications/base/controller/404',
'PhabricatorAuditActionConstants' => 'applications/audit/constants/action',
'PhabricatorAuditComment' => 'applications/audit/storage/auditcomment',
'PhabricatorAuditController' => 'applications/audit/controller/base',
'PhabricatorAuditDAO' => 'applications/audit/storage/base',
'PhabricatorAuditEditController' => 'applications/audit/controller/edit',
'PhabricatorAuditStatusConstants' => 'applications/audit/constants/status',
'PhabricatorAuthController' => 'applications/auth/controller/base',
'PhabricatorCalendarBrowseController' => 'applications/calendar/controller/browse',
'PhabricatorCalendarController' => 'applications/calendar/controller/base',
@ -1046,6 +1052,10 @@ phutil_register_library_map(array(
'ManiphestTransactionType' => 'ManiphestConstants',
'ManiphestView' => 'AphrontView',
'Phabricator404Controller' => 'PhabricatorController',
'PhabricatorAuditComment' => 'PhabricatorAuditDAO',
'PhabricatorAuditController' => 'PhabricatorController',
'PhabricatorAuditDAO' => 'PhabricatorLiskDAO',
'PhabricatorAuditEditController' => 'PhabricatorAuditController',
'PhabricatorAuthController' => 'PhabricatorController',
'PhabricatorCalendarBrowseController' => 'PhabricatorCalendarController',
'PhabricatorCalendarController' => 'PhabricatorController',

View file

@ -303,6 +303,11 @@ class AphrontDefaultApplicationConfiguration
),
),
'/audit/' => array(
'$' => 'PhabricatorAuditEditController',
'edit/$' => 'PhabricatorAuditEditController',
),
'/xhpast/' => array(
'$' => 'PhabricatorXHPASTViewRunController',
'view/(?P<id>\d+)/$'

View file

@ -0,0 +1,44 @@
<?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 PhabricatorAuditActionConstants {
const CONCERN = 'concern';
const ACCEPT = 'accept';
// TODO: enable comment
//const COMMENT = 'comment';
public static function getActionNameMap() {
static $map = array(
self::CONCERN => 'Have Concern',
self::ACCEPT => 'Accept',
);
return $map;
}
public static function getStatusNameMap() {
static $map = array(
self::CONCERN => PhabricatorAuditStatusConstants::CONCERNED,
self::ACCEPT => PhabricatorAuditStatusConstants::ACCEPTED,
);
return $map;
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/audit/constants/status');
phutil_require_source('PhabricatorAuditActionConstants.php');

View file

@ -0,0 +1,39 @@
<?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 PhabricatorAuditStatusConstants {
const NONE = '';
const AUDIT_NOT_REQUIRED = 'audit-not-required';
const AUDIT_REQUIRED = 'audit-required';
const CONCERNED = 'concerned';
const ACCEPTED = 'accepted';
public static function getStatusNameMap() {
static $map = array(
self::NONE => 'Not Apply',
self::AUDIT_NOT_REQUIRED => 'Audit Not Required',
self::AUDIT_REQUIRED => 'Audit Required',
self::CONCERNED => 'Concerned',
self::ACCEPTED => 'Accepted',
);
return $map;
}
}

View file

@ -0,0 +1,10 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_source('PhabricatorAuditStatusConstants.php');

View file

@ -0,0 +1,35 @@
<?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.
*/
abstract class PhabricatorAuditController extends PhabricatorController {
public function buildStandardPageResponse($view, array $data) {
$page = $this->buildStandardPageView();
$page->setApplicationName('Audit');
$page->setBaseURI('/audit/');
$page->setTitle(idx($data, 'title'));
$page->setGlyph("\xE2\x9C\x8D");
$page->appendChild($view);
$response = new AphrontWebpageResponse();
return $response->setContent($page->render());
}
}

View file

@ -0,0 +1,15 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/webpage');
phutil_require_module('phabricator', 'applications/base/controller/base');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditController.php');

View file

@ -0,0 +1,297 @@
<?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 PhabricatorAuditEditController extends PhabricatorAuditController {
private $request;
private $user;
private $commitPHID;
private $packagePHID;
public function processRequest() {
$this->request = $this->getRequest();
$this->user = $this->request->getUser();
$this->commitPHID = $this->request->getStr('c-phid');
$this->packagePHID = $this->request->getStr('p-phid');
$relationship = id(new PhabricatorOwnersPackageCommitRelationship())
->loadOneWhere(
'commitPHID = %s AND packagePHID=%s',
$this->commitPHID,
$this->packagePHID);
if (!$relationship) {
return new Aphront404Response();
}
$package = id(new PhabricatorOwnersPackage())->loadOneWhere(
"phid = %s",
$this->packagePHID);
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID = %d',
$package->getID());
$owners_phids = mpull($owners, 'getUserPHID');
if (!$this->user->getIsAdmin() &&
!in_array($this->user->getPHID(), $owners_phids)) {
return $this->buildStandardPageResponse(
id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_ERROR)
->setTitle("Only admin or owner of the package can audit the ".
"commit."),
array(
'title' => 'Audit a Commit',
));
}
if ($this->request->isFormPost()) {
return $this->saveAuditComments();
}
$package_link = phutil_render_tag(
'a',
array(
'href' => '/owners/package/'.$package->getID().'/',
),
phutil_escape_html($package->getName()));
$phids = array(
$this->commitPHID,
);
$loader = new PhabricatorObjectHandleData($phids);
$handles = $loader->loadHandles();
$objects = $loader->loadObjects();
$commit_handle = $handles[$this->commitPHID];
$commit_object = $objects[$this->commitPHID];
$commit_data = $commit_object->getCommitData();
$commit_epoch = $commit_handle->getTimeStamp();
$commit_datetime = phabricator_datetime($commit_epoch, $this->user);
$commit_link = $this->renderHandleLink($commit_handle);
$revision_author_phid = null;
$revision_reviewedby_phid = null;
$revision_link = null;
$revision_id = $commit_data->getCommitDetail('differential.revisionID');
if ($revision_id) {
$revision = id(new DifferentialRevision())->load($revision_id);
if ($revision) {
$revision->loadRelationships();
$revision_author_phid = $revision->getAuthorPHID();
$revision_reviewedby_phid = $revision->loadReviewedBy();
$revision_link = phutil_render_tag(
'a',
array(
'href' => '/D'.$revision->getID()
),
phutil_escape_html($revision->getTitle()));
}
}
$commit_author_phid = $commit_data->getCommitDetail('authorPHID');
$commit_reviewedby_phid = $commit_data->getCommitDetail('reviewerPHID');
$conn_r = id(new PhabricatorAuditComment())->establishConnection('r');
$latest_comment = queryfx_one(
$conn_r,
'SELECT * FROM %T
WHERE targetPHID = %s and actorPHID in (%Ls)
ORDER BY ID DESC LIMIT 1',
id(new PhabricatorAuditComment())->getTableName(),
$this->commitPHID,
$owners_phids);
$auditor_phid = $latest_comment['actorPHID'];
$user_phids = array_unique(array_filter(array(
$revision_author_phid,
$revision_reviewedby_phid,
$commit_author_phid,
$commit_reviewedby_phid,
$auditor_phid,
)));
$user_loader = new PhabricatorObjectHandleData($user_phids);
$user_handles = $user_loader->loadHandles();
if ($commit_author_phid && isset($handles[$commit_author_phid])) {
$commit_author_link = $handles[$commit_author_phid]->renderLink();
} else {
$commit_author_link = phutil_escape_html($commit_data->getAuthorName());
}
$reasons = $relationship->getAuditReasons();
$reasons = array_map('phutil_escape_html', $reasons);
$reasons = implode($reasons, '<br>');
$latest_comment_content = id(new AphrontFormTextAreaControl())
->setLabel('Audit comments')
->setName('latest_comments')
->setReadOnly(true)
->setValue($latest_comment['content']);
$latest_comment_epoch = $latest_comment['dateModified'];
$latest_comment_datetime =
phabricator_datetime($latest_comment_epoch, $this->user);
$select = id(new AphrontFormSelectControl())
->setLabel('Audit it')
->setName('action')
->setValue(PhabricatorAuditActionConstants::ACCEPT)
->setOptions(PhabricatorAuditActionConstants::getActionNameMap());
$comment = id(new AphrontFormTextAreaControl())
->setLabel('Audit comments')
->setName('comments')
->setCaption("Explain the audit.");
$submit = id(new AphrontFormSubmitControl())
->setValue('Save')
->addCancelButton('/owners/related/view/audit/?phid='.$this->packagePHID);
$form = id(new AphrontFormView())
->setUser($this->user)
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Package')
->setValue($package_link))
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Commit')
->setValue($commit_link))
->appendChild(new AphrontFormDividerControl())
->appendChild(id(new AphrontFormStaticControl())
->setLabel('Commit Summary')
->setValue(phutil_escape_html($commit_data->getSummary())))
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Commit Author')
->setValue($commit_author_link))
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Commit Reviewed By')
->setValue(
$this->renderHandleLink(
idx($user_handles, $commit_reviewedby_phid))))
->appendChild(id(new AphrontFormStaticControl())
->setLabel('Commit Time')
->setValue($commit_datetime))
->appendChild(new AphrontFormDividerControl())
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Revision')
->setValue($revision_link))
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Revision Author')
->setValue(
$this->renderHandleLink(idx($user_handles, $revision_author_phid))))
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Revision Reviewed By')
->setValue(
$this->renderHandleLink(
idx($user_handles, $revision_reviewedby_phid))))
->appendChild(new AphrontFormDividerControl())
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Audit Reasons')
->setValue($reasons))
->appendChild(id(new AphrontFormMarkupControl())
->setLabel('Latest Auditor')
->setValue($this->renderHandleLink(idx($user_handles, $auditor_phid))))
->appendChild(id(new AphrontFormStaticControl())
->setLabel('Latest Audit Status')
->setValue(idx(PhabricatorAuditStatusConstants::getStatusNameMap(),
$relationship->getAuditStatus())))
->appendChild(id(new AphrontFormStaticControl())
->setLabel('Latest Audit Time')
->setValue($latest_comment_datetime))
->appendChild($latest_comment_content)
->appendChild(new AphrontFormDividerControl())
->appendChild($select)
->appendChild($comment)
->appendChild($submit);
$panel = id(new AphrontPanelView())
->setHeader('Audit a Commit')
->setWidth(AphrontPanelView::WIDTH_WIDE)
->appendChild($form);
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Audit a Commit',
));
}
private function saveAuditComments() {
$action = $this->request->getStr('action');
$status_map = PhabricatorAuditActionConstants::getStatusNameMap();
$status = idx($status_map, $action, null);
if ($status === null) {
return $this->buildStandardPageResponse(
id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_ERROR)
->setTitle("Action {$action} is invalid."),
array(
'title' => 'Audit a Commit',
));
}
id(new PhabricatorAuditComment())
->setActorPHID($this->user->getPHID())
->setTargetPHID($this->commitPHID)
->setAction($action)
->setContent($this->request->getStr('comments'))
->save();
// Update the audit status for all the relationships <commit, package>
// where the package is owned by the user. When a user owns several
// packages and a commit touches all of them,It should be good enough for
// the user to approve it once to get all the relationships automatically
// updated.
$owned_packages = id(new PhabricatorOwnersOwner())->loadAllWhere(
'userPHID = %s',
$this->user->getPHID());
$owned_package_ids = mpull($owned_packages, 'getPackageID');
$conn_r = id(new PhabricatorOwnersPackage())->establishConnection('r');
$owned_package_phids = queryfx_all(
$conn_r,
'SELECT `phid` FROM %T WHERE id IN (%Ld)',
id(new PhabricatorOwnersPackage())->getTableName(),
$owned_package_ids);
$owned_package_phids = ipull($owned_package_phids, 'phid');
$relationships = id(new PhabricatorOwnersPackageCommitRelationship())
->loadAllWhere(
'commitPHID = %s AND packagePHID IN (%Ls)',
$this->commitPHID,
$owned_package_phids);
foreach ($relationships as $relationship) {
$relationship->setAuditStatus($status);
$relationship->save();
}
return id(new AphrontRedirectResponse())
->setURI(sprintf('/audit/edit/?c-phid=%s&p-phid=%s',
$this->commitPHID,
$this->packagePHID));
}
private function renderHandleLink($handle) {
if (!$handle) {
return null;
}
return phutil_render_tag(
'a',
array(
'href' => $handle->getURI(),
),
phutil_escape_html($handle->getName()));
}
}

View file

@ -0,0 +1,36 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/audit/constants/action');
phutil_require_module('phabricator', 'applications/audit/constants/status');
phutil_require_module('phabricator', 'applications/audit/controller/base');
phutil_require_module('phabricator', 'applications/audit/storage/auditcomment');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
phutil_require_module('phabricator', 'applications/owners/storage/owner');
phutil_require_module('phabricator', 'applications/owners/storage/package');
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
phutil_require_module('phabricator', 'applications/phid/handle/data');
phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/divider');
phutil_require_module('phabricator', 'view/form/control/markup');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/static');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorAuditEditController.php');

View file

@ -0,0 +1,37 @@
<?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 PhabricatorAuditComment extends PhabricatorAuditDAO {
protected $phid;
protected $actorPHID;
protected $targetPHID;
protected $action;
protected $content;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID('ACMT');
}
}

View file

@ -0,0 +1,13 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/audit/storage/base');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_source('PhabricatorAuditComment.php');

View file

@ -0,0 +1,25 @@
<?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.
*/
abstract class PhabricatorAuditDAO extends PhabricatorLiskDAO {
public function getApplicationName() {
return 'audit';
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/base/storage/lisk');
phutil_require_source('PhabricatorAuditDAO.php');

View file

@ -76,6 +76,11 @@ class PhabricatorOwnersDetailController extends PhabricatorOwnersController {
'Owners',
$owner_links);
$rows[] = array(
'Auditing',
$package->getAuditingEnabled() ? 'Enabled' : 'Disabled',
);
$rows[] = array(
'Related Commits',
phutil_render_tag(

View file

@ -47,6 +47,7 @@ class PhabricatorOwnersEditController extends PhabricatorOwnersController {
if ($request->isFormPost()) {
$package->setName($request->getStr('name'));
$package->setDescription($request->getStr('description'));
$package->setAuditingEnabled($request->getStr('auditing') === 'enabled');
$primary = $request->getArr('primary');
$primary = reset($primary);
@ -207,6 +208,18 @@ class PhabricatorOwnersEditController extends PhabricatorOwnersController {
->setName('owners')
->setValue($token_all_owners)
->setError($e_owners))
->appendChild(
id(new AphrontFormSelectControl())
->setName('auditing')
->setLabel('Auditing')
->setOptions(array(
'disabled' => 'Disabled',
'enabled' => 'Enabled',
))
->setValue(
$package->getAuditingEnabled()
? 'enabled'
: 'disabled'))
->appendChild(
'<h1>Paths</h1>'.
'<div class="aphront-form-inset" id="path-editor">'.

View file

@ -17,6 +17,7 @@ phutil_require_module('phabricator', 'infrastructure/javelin/api');
phutil_require_module('phabricator', 'infrastructure/javelin/markup');
phutil_require_module('phabricator', 'view/control/typeahead');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/text');
phutil_require_module('phabricator', 'view/form/control/textarea');

View file

@ -23,6 +23,7 @@ class PhabricatorOwnerRelatedListController
private $user;
private $view;
private $packagePHID;
private $auditStatus;
public function willProcessRequest(array $data) {
$this->view = idx($data, 'view', 'all');
@ -34,15 +35,18 @@ class PhabricatorOwnerRelatedListController
if ($this->request->isFormPost()) {
$package_phids = $this->request->getArr('search_packages');
$package_phid = head($package_phids);
$status = $this->request->getStr('search_status');
return id(new AphrontRedirectResponse())
->setURI(
$this->request
->getRequestURI()
->alter('phid', $package_phid));
->alter('phid', $package_phid)
->alter('status', $status));
}
$this->user = $this->request->getUser();
$this->packagePHID = nonempty($this->request->getStr('phid'), null);
$this->auditStatus = $this->request->getStr('status', 'needaudit');
$search_view = $this->renderSearchView();
$list_panel = $this->renderListPanel();
@ -69,6 +73,16 @@ class PhabricatorOwnerRelatedListController
$package = id(new PhabricatorOwnersPackage())->loadOneWhere(
"phid = %s",
$this->packagePHID);
if ($this->view === 'audit' && !$package->getAuditingEnabled()) {
return id(new AphrontErrorView())
->setSeverity(AphrontErrorView::SEVERITY_NOTICE)
->setTitle("Package doesn't have auditing enabled. ".
"Please choose another one.");
}
$conn_r = id(new PhabricatorOwnersPackageCommitRelationship())
->establishConnection('r');
$status_arr = $this->getStatusArr();
$offset = $this->request->getInt('offset', 0);
$pager = new AphrontPagerView();
@ -76,26 +90,17 @@ class PhabricatorOwnerRelatedListController
$pager->setOffset($offset);
$pager->setURI($this->request->getRequestURI(), 'offset');
$conn_r = id(new PhabricatorOwnersPackageCommitRelationship())
->establishConnection('r');
switch ($this->view) {
case 'all':
$data = queryfx_all(
$conn_r,
'SELECT commitPHID FROM %T
WHERE packagePHID = %s
ORDER BY id DESC
LIMIT %d, %d',
id(new PhabricatorOwnersPackageCommitRelationship())->getTableName(),
$package->getPHID(),
$pager->getOffset(),
$pager->getPageSize() + 1);
break;
default:
throw new Exception("view {$this->view} not recognized");
}
$data = queryfx_all(
$conn_r,
'SELECT commitPHID, auditStatus, auditReasons FROM %T
WHERE packagePHID = %s AND auditStatus in (%Ls)
ORDER BY id DESC
LIMIT %d, %d',
id(new PhabricatorOwnersPackageCommitRelationship())->getTableName(),
$package->getPHID(),
$status_arr,
$pager->getOffset(),
$pager->getPageSize() + 1);
$data = $pager->sliceResults($data);
$data = ipull($data, null, 'commitPHID');
@ -106,9 +111,50 @@ class PhabricatorOwnerRelatedListController
return $list_panel;
}
private function getStatusArr() {
switch ($this->view) {
case 'all':
$status_arr =
array_keys(PhabricatorAuditStatusConstants::getStatusNameMap());
break;
case 'audit':
switch ($this->auditStatus) {
case 'needaudit':
$status_arr =
array(
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
PhabricatorAuditStatusConstants::CONCERNED,
);
break;
case 'accepted':
$status_arr =
array(
PhabricatorAuditStatusConstants::ACCEPTED,
);
break;
case 'all':
$status_arr =
array(
PhabricatorAuditStatusConstants::AUDIT_REQUIRED,
PhabricatorAuditStatusConstants::CONCERNED,
PhabricatorAuditStatusConstants::ACCEPTED,
);
break;
default:
throw new Exception("Status {$this->auditStatus} not recognized");
}
break;
default:
throw new Exception("view {$this->view} not recognized");
}
return $status_arr;
}
private function renderSideNav() {
$views = array(
'all' => 'Related to Package',
'audit' => 'Needs Attention',
);
$query = null;
@ -155,6 +201,21 @@ class PhabricatorOwnerRelatedListController
->setValue($view_packages)
->setLimit(1));
if ($this->view === 'audit') {
$select_map = array(
'needaudit' => 'Needs Audit',
'accepted' => 'Accepted',
'all' => 'All',
);
$select = id(new AphrontFormSelectControl())
->setLabel('Audit Status')
->setName('search_status')
->setOptions($select_map)
->setValue($this->auditStatus);
$search_form->appendChild($select);
}
$search_form->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Search'));
@ -170,6 +231,17 @@ class PhabricatorOwnerRelatedListController
$handles = $loader->loadHandles();
$objects = $loader->loadObjects();
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID = %d',
$package->getID());
$owners_phids = mpull($owners, 'getUserPHID');
if ($this->user->getIsAdmin() ||
in_array($this->user->getPHID(), $owners_phids)) {
$allowed_to_audit = true;
} else {
$allowed_to_audit = false;
}
$rows = array();
foreach ($commit_phids as $commit_phid) {
$handle = $handles[$commit_phid];
@ -191,6 +263,33 @@ class PhabricatorOwnerRelatedListController
phutil_escape_html($commit_data->getSummary()),
);
if ($this->view === 'audit') {
$relationship = $data[$commit_phid];
$status_link = phutil_escape_html(
idx(PhabricatorAuditStatusConstants::getStatusNameMap(),
$relationship['auditStatus']));
if ($allowed_to_audit)
$status_link = phutil_render_tag(
'a',
array(
'href' => sprintf('/audit/edit/?c-phid=%s&p-phid=%s',
idx($relationship, 'commitPHID'),
$this->packagePHID),
),
$status_link);
$reasons = json_decode($relationship['auditReasons'], true);
$reasons = array_map('phutil_escape_html', $reasons);
$reasons = implode($reasons, '<br>');
$row = array_merge(
$row,
array(
$status_link,
$reasons,
));
}
$rows[] = $row;
}
@ -202,6 +301,14 @@ class PhabricatorOwnerRelatedListController
'Time',
'Summary',
);
if ($this->view === 'audit') {
$headers = array_merge(
$headers,
array(
'Audit Status',
'Audit Reasons',
));
}
$commit_table->setHeaders($headers);
$column_classes =
@ -211,6 +318,14 @@ class PhabricatorOwnerRelatedListController
'right',
'wide',
);
if ($this->view === 'audit') {
$column_classes = array_merge(
$column_classes,
array(
'',
'',
));
}
$commit_table->setColumnClasses($column_classes);
$list_panel = new AphrontPanelView();
@ -221,7 +336,8 @@ class PhabricatorOwnerRelatedListController
'href' => '/owners/package/'.$package->getID().'/',
),
phutil_escape_html($package->getName())).
'"');
'"'.
($this->view === 'audit' ? ' and need attention' : ''));
$list_panel->appendChild($commit_table);
return $list_panel;

View file

@ -7,7 +7,9 @@
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/audit/constants/status');
phutil_require_module('phabricator', 'applications/owners/controller/base');
phutil_require_module('phabricator', 'applications/owners/storage/owner');
phutil_require_module('phabricator', 'applications/owners/storage/package');
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
phutil_require_module('phabricator', 'applications/phid/handle/data');
@ -15,6 +17,7 @@ phutil_require_module('phabricator', 'storage/queryfx');
phutil_require_module('phabricator', 'view/control/pager');
phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/select');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/tokenizer');
phutil_require_module('phabricator', 'view/form/error');

View file

@ -20,6 +20,7 @@ class PhabricatorOwnersPackage extends PhabricatorOwnersDAO {
protected $phid;
protected $name;
protected $auditingEnabled;
protected $description;
protected $primaryOwnerPHID;

View file

@ -20,10 +20,15 @@ class PhabricatorOwnersPackageCommitRelationship extends PhabricatorOwnersDAO {
protected $packagePHID;
protected $commitPHID;
protected $auditReasons = array();
protected $auditStatus;
public function getConfiguration() {
return array(
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_SERIALIZATION => array(
'auditReasons' => self::SERIALIZATION_JSON,
),
) + parent::getConfiguration();
}

View file

@ -34,5 +34,6 @@ final class PhabricatorPHIDConstants {
const PHID_TYPE_POLL = 'POLL';
const PHID_TYPE_WIKI = 'WIKI';
const PHID_TYPE_APRJ = 'APRJ';
const PHID_TYPE_ACMT = 'ACMT';
}

View file

@ -39,12 +39,84 @@ class PhabricatorRepositoryCommitOwnersWorker
$package->getPHID(),
$commit->getPHID());
// Don't update relationship if it exists already
if (!$relationship) {
if ($package->getAuditingEnabled()) {
$reasons = $this->checkAuditReasons($commit, $package);
if ($reasons) {
$audit_status =
PhabricatorAuditStatusConstants::AUDIT_REQUIRED;
} else {
$audit_status =
PhabricatorAuditStatusConstants::AUDIT_NOT_REQUIRED;
}
} else {
$reasons = array();
$audit_status = PhabricatorAuditStatusConstants::NONE;
}
$relationship = new PhabricatorOwnersPackageCommitRelationship();
$relationship->setPackagePHID($package->getPHID());
$relationship->setCommitPHID($commit->getPHID());
$relationship->setAuditReasons($reasons);
$relationship->setAuditStatus($audit_status);
$relationship->save();
}
}
}
private function checkAuditReasons(
PhabricatorRepositoryCommit $commit,
PhabricatorOwnersPackage $package) {
$data = id(new PhabricatorRepositoryCommitData())->loadOneWhere(
'commitID = %d',
$commit->getID());
$reasons = array();
$commit_author_phid = $data->getCommitDetail('authorPHID');
if (!$commit_author_phid) {
$reasons[] = "Commit Author Not Recognized";
}
$revision_id = $data->getCommitDetail('differential.revisionID');
$revision_author_phid = null;
if ($revision_id) {
$revision = id(new DifferentialRevision())->load($revision_id);
if ($revision) {
$revision->loadRelationships();
$revision_author_phid = $revision->getAuthorPHID();
$revision_reviewedby_phid = $revision->loadReviewedBy();
$commit_reviewedby_phid = $data->getCommitDetail('reviewerPHID');
if ($revision_author_phid !== $commit_author_phid) {
$reasons[] = "Author Not Matching with Revision";
}
if ($revision_reviewedby_phid !== $commit_reviewedby_phid) {
$reasons[] = "ReviewedBy Not Matching with Revision";
}
} else {
$reasons[] = "Revision Not Found";
}
} else {
$reasons[] = "No Revision Specified";
}
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID = %d',
$package->getID());
$owners_phids = mpull($owners, 'getUserPHID');
if (!($commit_author_phid && in_array($commit_author_phid, $owners_phids) ||
$revision_author_phid && in_array($revision_author_phid,
$owners_phids))) {
$reasons[] = "Owners Not Involved";
}
return $reasons;
}
}

View file

@ -6,9 +6,13 @@
phutil_require_module('phabricator', 'applications/audit/constants/status');
phutil_require_module('phabricator', 'applications/differential/storage/revision');
phutil_require_module('phabricator', 'applications/owners/query/path');
phutil_require_module('phabricator', 'applications/owners/storage/owner');
phutil_require_module('phabricator', 'applications/owners/storage/package');
phutil_require_module('phabricator', 'applications/owners/storage/packagecommitrelationship');
phutil_require_module('phabricator', 'applications/repository/storage/commitdata');
phutil_require_module('phabricator', 'applications/repository/worker/base');
phutil_require_module('phutil', 'utils');

View file

@ -23,13 +23,24 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
const HEIGHT_VERY_TALL = 'very-tall';
private $height;
private $readOnly;
private $enableDragAndDropFileUploads;
public function setHeight($height) {
$this->height = $height;
return $this;
}
public function setReadOnly($read_only) {
$this->readOnly = $read_only;
return $this;
}
protected function getReadOnly() {
return $this->readOnly;
}
protected function getCustomControlClass() {
return 'aphront-form-control-textarea';
}
@ -69,6 +80,7 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
array(
'name' => $this->getName(),
'disabled' => $this->getDisabled() ? 'disabled' : null,
'readonly' => $this->getReadonly() ? 'readonly' : null,
'class' => $height_class,
'style' => $this->getControlStyle(),
'id' => $id,