mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Track how many columns use a particular trigger
Summary: Ref T5474. In 99% of cases, a separate "archived/active" status for triggers probably doesn't make much sense: there's not much reason to ever disable/archive a trigger explcitly, and the archival rule is really just "is this trigger used by anything?". (The one reason I can think of to disable a trigger manually is because you want to put something in a column and skip trigger rules, but you can already do this from the task detail page anyway, and disabling the trigger globally is a bad way to accomplish this if it's in use by other columns.) Instead of adding a separate "status", just track how many columns a trigger is used by and consider it "inactive" if it is not used by any active columns. Test Plan: This is slightly hard to test exhaustively since you can't share a trigger across multiple columns right now, but: rebuild indexes, poked around the trigger list and trigger details, added/removed triggers. Reviewers: amckinley Reviewed By: amckinley Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T5474 Differential Revision: https://secure.phabricator.com/D20308
This commit is contained in:
parent
bfa5ffe8a1
commit
47856dc93f
12 changed files with 360 additions and 11 deletions
8
resources/sql/autopatches/20190322.triggers.01.usage.sql
Normal file
8
resources/sql/autopatches/20190322.triggers.01.usage.sql
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_project.project_triggerusage (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
triggerPHID VARBINARY(64) NOT NULL,
|
||||||
|
examplePHID VARBINARY(64),
|
||||||
|
columnCount INT UNSIGNED NOT NULL,
|
||||||
|
activeColumnCount INT UNSIGNED NOT NULL,
|
||||||
|
UNIQUE KEY `key_trigger` (triggerPHID)
|
||||||
|
) ENGINE=InnoDB DEFAULT CHARSET={$CHARSET} COLLATE {$COLLATE_TEXT};
|
|
@ -4193,6 +4193,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectTriggerTransactionQuery' => 'applications/project/query/PhabricatorProjectTriggerTransactionQuery.php',
|
'PhabricatorProjectTriggerTransactionQuery' => 'applications/project/query/PhabricatorProjectTriggerTransactionQuery.php',
|
||||||
'PhabricatorProjectTriggerTransactionType' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerTransactionType.php',
|
'PhabricatorProjectTriggerTransactionType' => 'applications/project/xaction/trigger/PhabricatorProjectTriggerTransactionType.php',
|
||||||
'PhabricatorProjectTriggerUnknownRule' => 'applications/project/trigger/PhabricatorProjectTriggerUnknownRule.php',
|
'PhabricatorProjectTriggerUnknownRule' => 'applications/project/trigger/PhabricatorProjectTriggerUnknownRule.php',
|
||||||
|
'PhabricatorProjectTriggerUsage' => 'applications/project/storage/PhabricatorProjectTriggerUsage.php',
|
||||||
|
'PhabricatorProjectTriggerUsageIndexEngineExtension' => 'applications/project/engineextension/PhabricatorProjectTriggerUsageIndexEngineExtension.php',
|
||||||
'PhabricatorProjectTriggerViewController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerViewController.php',
|
'PhabricatorProjectTriggerViewController' => 'applications/project/controller/trigger/PhabricatorProjectTriggerViewController.php',
|
||||||
'PhabricatorProjectTypeTransaction' => 'applications/project/xaction/PhabricatorProjectTypeTransaction.php',
|
'PhabricatorProjectTypeTransaction' => 'applications/project/xaction/PhabricatorProjectTypeTransaction.php',
|
||||||
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
|
'PhabricatorProjectUIEventListener' => 'applications/project/events/PhabricatorProjectUIEventListener.php',
|
||||||
|
@ -10307,6 +10309,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectDAO',
|
'PhabricatorProjectDAO',
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
|
'PhabricatorIndexableInterface',
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
),
|
),
|
||||||
'PhabricatorProjectTriggerController' => 'PhabricatorProjectController',
|
'PhabricatorProjectTriggerController' => 'PhabricatorProjectController',
|
||||||
|
@ -10328,6 +10331,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorProjectTriggerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'PhabricatorProjectTriggerTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'PhabricatorProjectTriggerTransactionType' => 'PhabricatorModularTransactionType',
|
'PhabricatorProjectTriggerTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'PhabricatorProjectTriggerUnknownRule' => 'PhabricatorProjectTriggerRule',
|
'PhabricatorProjectTriggerUnknownRule' => 'PhabricatorProjectTriggerRule',
|
||||||
|
'PhabricatorProjectTriggerUsage' => 'PhabricatorProjectDAO',
|
||||||
|
'PhabricatorProjectTriggerUsageIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||||
'PhabricatorProjectTriggerViewController' => 'PhabricatorProjectTriggerController',
|
'PhabricatorProjectTriggerViewController' => 'PhabricatorProjectTriggerController',
|
||||||
'PhabricatorProjectTypeTransaction' => 'PhabricatorProjectTransactionType',
|
'PhabricatorProjectTypeTransaction' => 'PhabricatorProjectTransactionType',
|
||||||
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
|
'PhabricatorProjectUIEventListener' => 'PhabricatorEventListener',
|
||||||
|
|
|
@ -71,17 +71,23 @@ final class PhabricatorProjectTriggerViewController
|
||||||
// so we load only the columns they can actually see, but have a list of
|
// so we load only the columns they can actually see, but have a list of
|
||||||
// all the impacted column PHIDs.
|
// all the impacted column PHIDs.
|
||||||
|
|
||||||
|
// (We're also exposing the status of columns the user might not be able
|
||||||
|
// to see. This technically violates policy, but the trigger usage table
|
||||||
|
// hints at it anyway and it seems unlikely to ever have any security
|
||||||
|
// impact, but is useful in assessing whether a trigger is really in use
|
||||||
|
// or not.)
|
||||||
|
|
||||||
$omnipotent_viewer = PhabricatorUser::getOmnipotentUser();
|
$omnipotent_viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
$all_columns = id(new PhabricatorProjectColumnQuery())
|
$all_columns = id(new PhabricatorProjectColumnQuery())
|
||||||
->setViewer($omnipotent_viewer)
|
->setViewer($omnipotent_viewer)
|
||||||
->withTriggerPHIDs(array($trigger->getPHID()))
|
->withTriggerPHIDs(array($trigger->getPHID()))
|
||||||
->execute();
|
->execute();
|
||||||
$column_phids = mpull($all_columns, 'getPHID');
|
$column_map = mpull($all_columns, 'getStatus', 'getPHID');
|
||||||
|
|
||||||
if ($column_phids) {
|
if ($column_map) {
|
||||||
$visible_columns = id(new PhabricatorProjectColumnQuery())
|
$visible_columns = id(new PhabricatorProjectColumnQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withPHIDs($column_phids)
|
->withPHIDs(array_keys($column_map))
|
||||||
->execute();
|
->execute();
|
||||||
$visible_columns = mpull($visible_columns, null, 'getPHID');
|
$visible_columns = mpull($visible_columns, null, 'getPHID');
|
||||||
} else {
|
} else {
|
||||||
|
@ -89,7 +95,7 @@ final class PhabricatorProjectTriggerViewController
|
||||||
}
|
}
|
||||||
|
|
||||||
$rows = array();
|
$rows = array();
|
||||||
foreach ($column_phids as $column_phid) {
|
foreach ($column_map as $column_phid => $column_status) {
|
||||||
$column = idx($visible_columns, $column_phid);
|
$column = idx($visible_columns, $column_phid);
|
||||||
|
|
||||||
if ($column) {
|
if ($column) {
|
||||||
|
@ -113,7 +119,18 @@ final class PhabricatorProjectTriggerViewController
|
||||||
$column_name = phutil_tag('em', array(), pht('Restricted Column'));
|
$column_name = phutil_tag('em', array(), pht('Restricted Column'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($column_status == PhabricatorProjectColumn::STATUS_ACTIVE) {
|
||||||
|
$status_icon = id(new PHUIIconView())
|
||||||
|
->setIcon('fa-columns', 'blue')
|
||||||
|
->setTooltip(pht('Active Column'));
|
||||||
|
} else {
|
||||||
|
$status_icon = id(new PHUIIconView())
|
||||||
|
->setIcon('fa-eye-slash', 'grey')
|
||||||
|
->setTooltip(pht('Hidden Column'));
|
||||||
|
}
|
||||||
|
|
||||||
$rows[] = array(
|
$rows[] = array(
|
||||||
|
$status_icon,
|
||||||
$project_name,
|
$project_name,
|
||||||
$column_name,
|
$column_name,
|
||||||
);
|
);
|
||||||
|
@ -123,11 +140,13 @@ final class PhabricatorProjectTriggerViewController
|
||||||
->setNoDataString(pht('This trigger is not used by any columns.'))
|
->setNoDataString(pht('This trigger is not used by any columns.'))
|
||||||
->setHeaders(
|
->setHeaders(
|
||||||
array(
|
array(
|
||||||
|
null,
|
||||||
pht('Project'),
|
pht('Project'),
|
||||||
pht('Column'),
|
pht('Column'),
|
||||||
))
|
))
|
||||||
->setColumnClasses(
|
->setColumnClasses(
|
||||||
array(
|
array(
|
||||||
|
null,
|
||||||
null,
|
null,
|
||||||
'wide pri',
|
'wide pri',
|
||||||
));
|
));
|
||||||
|
|
|
@ -27,4 +27,8 @@ final class PhabricatorProjectTriggerEditor
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function supportsSearch() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectTriggerUsageIndexEngineExtension
|
||||||
|
extends PhabricatorIndexEngineExtension {
|
||||||
|
|
||||||
|
const EXTENSIONKEY = 'trigger.usage';
|
||||||
|
|
||||||
|
public function getExtensionName() {
|
||||||
|
return pht('Trigger Usage');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldIndexObject($object) {
|
||||||
|
if (!($object instanceof PhabricatorProjectTrigger)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function indexObject(
|
||||||
|
PhabricatorIndexEngine $engine,
|
||||||
|
$object) {
|
||||||
|
|
||||||
|
$usage_table = new PhabricatorProjectTriggerUsage();
|
||||||
|
$column_table = new PhabricatorProjectColumn();
|
||||||
|
|
||||||
|
$conn_w = $object->establishConnection('w');
|
||||||
|
|
||||||
|
$active_statuses = array(
|
||||||
|
PhabricatorProjectColumn::STATUS_ACTIVE,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Select summary information to populate the usage index. When picking
|
||||||
|
// an "examplePHID", we try to pick an active column.
|
||||||
|
$row = queryfx_one(
|
||||||
|
$conn_w,
|
||||||
|
'SELECT phid, COUNT(*) N, SUM(IF(status IN (%Ls), 1, 0)) M FROM %R
|
||||||
|
WHERE triggerPHID = %s
|
||||||
|
ORDER BY IF(status IN (%Ls), 1, 0) DESC, id ASC',
|
||||||
|
$active_statuses,
|
||||||
|
$column_table,
|
||||||
|
$object->getPHID(),
|
||||||
|
$active_statuses);
|
||||||
|
if ($row) {
|
||||||
|
$example_phid = $row['phid'];
|
||||||
|
$column_count = $row['N'];
|
||||||
|
$active_count = $row['M'];
|
||||||
|
} else {
|
||||||
|
$example_phid = null;
|
||||||
|
$column_count = 0;
|
||||||
|
$active_count = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'INSERT INTO %R (triggerPHID, examplePHID, columnCount, activeColumnCount)
|
||||||
|
VALUES (%s, %ns, %d, %d)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
examplePHID = VALUES(examplePHID),
|
||||||
|
columnCount = VALUES(columnCount),
|
||||||
|
activeColumnCount = VALUES(activeColumnCount)',
|
||||||
|
$usage_table,
|
||||||
|
$object->getPHID(),
|
||||||
|
$example_phid,
|
||||||
|
$column_count,
|
||||||
|
$active_count);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -5,6 +5,10 @@ final class PhabricatorProjectTriggerQuery
|
||||||
|
|
||||||
private $ids;
|
private $ids;
|
||||||
private $phids;
|
private $phids;
|
||||||
|
private $activeColumnMin;
|
||||||
|
private $activeColumnMax;
|
||||||
|
|
||||||
|
private $needUsage;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -16,6 +20,17 @@ final class PhabricatorProjectTriggerQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needUsage($need_usage) {
|
||||||
|
$this->needUsage = $need_usage;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function withActiveColumnCountBetween($min, $max) {
|
||||||
|
$this->activeColumnMin = $min;
|
||||||
|
$this->activeColumnMax = $max;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new PhabricatorProjectTrigger();
|
return new PhabricatorProjectTrigger();
|
||||||
}
|
}
|
||||||
|
@ -30,22 +45,91 @@ final class PhabricatorProjectTriggerQuery
|
||||||
if ($this->ids !== null) {
|
if ($this->ids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'id IN (%Ld)',
|
'trigger.id IN (%Ld)',
|
||||||
$this->ids);
|
$this->ids);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->phids !== null) {
|
if ($this->phids !== null) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'phid IN (%Ls)',
|
'trigger.phid IN (%Ls)',
|
||||||
$this->phids);
|
$this->phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->activeColumnMin !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'trigger_usage.activeColumnCount >= %d',
|
||||||
|
$this->activeColumnMin);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->activeColumnMax !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'trigger_usage.activeColumnCount <= %d',
|
||||||
|
$this->activeColumnMax);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
|
||||||
|
$joins = parent::buildJoinClauseParts($conn);
|
||||||
|
|
||||||
|
if ($this->shouldJoinUsageTable()) {
|
||||||
|
$joins[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'JOIN %R trigger_usage ON trigger.phid = trigger_usage.triggerPHID',
|
||||||
|
new PhabricatorProjectTriggerUsage());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $joins;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function shouldJoinUsageTable() {
|
||||||
|
if ($this->activeColumnMin !== null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($this->activeColumnMax !== null) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function didFilterPage(array $triggers) {
|
||||||
|
if ($this->needUsage) {
|
||||||
|
$usage_map = id(new PhabricatorProjectTriggerUsage())->loadAllWhere(
|
||||||
|
'triggerPHID IN (%Ls)',
|
||||||
|
mpull($triggers, 'getPHID'));
|
||||||
|
$usage_map = mpull($usage_map, null, 'getTriggerPHID');
|
||||||
|
|
||||||
|
foreach ($triggers as $trigger) {
|
||||||
|
$trigger_phid = $trigger->getPHID();
|
||||||
|
|
||||||
|
$usage = idx($usage_map, $trigger_phid);
|
||||||
|
if (!$usage) {
|
||||||
|
$usage = id(new PhabricatorProjectTriggerUsage())
|
||||||
|
->setTriggerPHID($trigger_phid)
|
||||||
|
->setExamplePHID(null)
|
||||||
|
->setColumnCount(0)
|
||||||
|
->setActiveColumnCount(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
$trigger->attachUsage($usage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $triggers;
|
||||||
|
}
|
||||||
|
|
||||||
public function getQueryApplicationClass() {
|
public function getQueryApplicationClass() {
|
||||||
return 'PhabricatorProjectApplication';
|
return 'PhabricatorProjectApplication';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getPrimaryTableAlias() {
|
||||||
|
return 'trigger';
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,16 +12,33 @@ final class PhabricatorProjectTriggerSearchEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
public function newQuery() {
|
public function newQuery() {
|
||||||
return new PhabricatorProjectTriggerQuery();
|
return id(new PhabricatorProjectTriggerQuery())
|
||||||
|
->needUsage(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildCustomSearchFields() {
|
protected function buildCustomSearchFields() {
|
||||||
return array();
|
return array(
|
||||||
|
id(new PhabricatorSearchThreeStateField())
|
||||||
|
->setLabel(pht('Active'))
|
||||||
|
->setKey('isActive')
|
||||||
|
->setOptions(
|
||||||
|
pht('(Show All)'),
|
||||||
|
pht('Show Only Active Triggers'),
|
||||||
|
pht('Show Only Inactive Triggers')),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildQueryFromParameters(array $map) {
|
protected function buildQueryFromParameters(array $map) {
|
||||||
$query = $this->newQuery();
|
$query = $this->newQuery();
|
||||||
|
|
||||||
|
if ($map['isActive'] !== null) {
|
||||||
|
if ($map['isActive']) {
|
||||||
|
$query->withActiveColumnCountBetween(1, null);
|
||||||
|
} else {
|
||||||
|
$query->withActiveColumnCountBetween(null, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -32,7 +49,8 @@ final class PhabricatorProjectTriggerSearchEngine
|
||||||
protected function getBuiltinQueryNames() {
|
protected function getBuiltinQueryNames() {
|
||||||
$names = array();
|
$names = array();
|
||||||
|
|
||||||
$names['all'] = pht('All');
|
$names['active'] = pht('Active Triggers');
|
||||||
|
$names['all'] = pht('All Triggers');
|
||||||
|
|
||||||
return $names;
|
return $names;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +60,8 @@ final class PhabricatorProjectTriggerSearchEngine
|
||||||
$query->setQueryKey($query_key);
|
$query->setQueryKey($query_key);
|
||||||
|
|
||||||
switch ($query_key) {
|
switch ($query_key) {
|
||||||
|
case 'active':
|
||||||
|
return $query->setParameter('isActive', true);
|
||||||
case 'all':
|
case 'all':
|
||||||
return $query;
|
return $query;
|
||||||
}
|
}
|
||||||
|
@ -56,13 +76,73 @@ final class PhabricatorProjectTriggerSearchEngine
|
||||||
assert_instances_of($triggers, 'PhabricatorProjectTrigger');
|
assert_instances_of($triggers, 'PhabricatorProjectTrigger');
|
||||||
$viewer = $this->requireViewer();
|
$viewer = $this->requireViewer();
|
||||||
|
|
||||||
|
$example_phids = array();
|
||||||
|
foreach ($triggers as $trigger) {
|
||||||
|
$example_phid = $trigger->getUsage()->getExamplePHID();
|
||||||
|
if ($example_phid) {
|
||||||
|
$example_phids[] = $example_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$handles = $viewer->loadHandles($example_phids);
|
||||||
|
|
||||||
$list = id(new PHUIObjectItemListView())
|
$list = id(new PHUIObjectItemListView())
|
||||||
->setViewer($viewer);
|
->setViewer($viewer);
|
||||||
foreach ($triggers as $trigger) {
|
foreach ($triggers as $trigger) {
|
||||||
|
$usage = $trigger->getUsage();
|
||||||
|
|
||||||
|
$column_handle = null;
|
||||||
|
$have_column = false;
|
||||||
|
$example_phid = $usage->getExamplePHID();
|
||||||
|
if ($example_phid) {
|
||||||
|
$column_handle = $handles[$example_phid];
|
||||||
|
if ($column_handle->isComplete()) {
|
||||||
|
if (!$column_handle->getPolicyFiltered()) {
|
||||||
|
$have_column = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$column_count = $usage->getColumnCount();
|
||||||
|
$active_count = $usage->getActiveColumnCount();
|
||||||
|
|
||||||
|
if ($have_column) {
|
||||||
|
if ($active_count > 1) {
|
||||||
|
$usage_description = pht(
|
||||||
|
'Used on %s and %s other active column(s).',
|
||||||
|
$column_handle->renderLink(),
|
||||||
|
new PhutilNumber($active_count - 1));
|
||||||
|
} else if ($column_count > 1) {
|
||||||
|
$usage_description = pht(
|
||||||
|
'Used on %s and %s other column(s).',
|
||||||
|
$column_handle->renderLink(),
|
||||||
|
new PhutilNumber($column_count - 1));
|
||||||
|
} else {
|
||||||
|
$usage_description = pht(
|
||||||
|
'Used on %s.',
|
||||||
|
$column_handle->renderLink());
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if ($active_count) {
|
||||||
|
$usage_description = pht(
|
||||||
|
'Used on %s active column(s).',
|
||||||
|
new PhutilNumber($active_count));
|
||||||
|
} else if ($column_count) {
|
||||||
|
$usage_description = pht(
|
||||||
|
'Used on %s column(s).',
|
||||||
|
new PhutilNumber($column_count));
|
||||||
|
} else {
|
||||||
|
$usage_description = pht(
|
||||||
|
'Unused trigger.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
$item = id(new PHUIObjectItemView())
|
$item = id(new PHUIObjectItemView())
|
||||||
->setObjectName($trigger->getObjectName())
|
->setObjectName($trigger->getObjectName())
|
||||||
->setHeader($trigger->getDisplayName())
|
->setHeader($trigger->getDisplayName())
|
||||||
->setHref($trigger->getURI());
|
->setHref($trigger->getURI())
|
||||||
|
->addAttribute($usage_description)
|
||||||
|
->setDisabled(!$active_count);
|
||||||
|
|
||||||
$list->addItem($item);
|
$list->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,6 +5,7 @@ final class PhabricatorProjectTrigger
|
||||||
implements
|
implements
|
||||||
PhabricatorApplicationTransactionInterface,
|
PhabricatorApplicationTransactionInterface,
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
|
PhabricatorIndexableInterface,
|
||||||
PhabricatorDestructibleInterface {
|
PhabricatorDestructibleInterface {
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
|
@ -12,6 +13,7 @@ final class PhabricatorProjectTrigger
|
||||||
protected $editPolicy;
|
protected $editPolicy;
|
||||||
|
|
||||||
private $triggerRules;
|
private $triggerRules;
|
||||||
|
private $usage = self::ATTACHABLE;
|
||||||
|
|
||||||
public static function initializeNewTrigger() {
|
public static function initializeNewTrigger() {
|
||||||
$default_edit = PhabricatorPolicies::POLICY_USER;
|
$default_edit = PhabricatorPolicies::POLICY_USER;
|
||||||
|
@ -257,6 +259,15 @@ final class PhabricatorProjectTrigger
|
||||||
return $sounds;
|
return $sounds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getUsage() {
|
||||||
|
return $this->assertAttached($this->usage);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachUsage(PhabricatorProjectTriggerUsage $usage) {
|
||||||
|
$this->usage = $usage;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||||
|
|
||||||
|
@ -310,6 +321,13 @@ final class PhabricatorProjectTrigger
|
||||||
new PhabricatorProjectColumn(),
|
new PhabricatorProjectColumn(),
|
||||||
$this->getPHID());
|
$this->getPHID());
|
||||||
|
|
||||||
|
// Remove the usage index row for this trigger, if one exists.
|
||||||
|
queryfx(
|
||||||
|
$conn,
|
||||||
|
'DELETE FROM %R WHERE triggerPHID = %s',
|
||||||
|
new PhabricatorProjectTriggerUsage(),
|
||||||
|
$this->getPHID());
|
||||||
|
|
||||||
$this->delete();
|
$this->delete();
|
||||||
|
|
||||||
$this->saveTransaction();
|
$this->saveTransaction();
|
||||||
|
|
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorProjectTriggerUsage
|
||||||
|
extends PhabricatorProjectDAO {
|
||||||
|
|
||||||
|
protected $triggerPHID;
|
||||||
|
protected $examplePHID;
|
||||||
|
protected $columnCount;
|
||||||
|
protected $activeColumnCount;
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_TIMESTAMPS => false,
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'examplePHID' => 'phid?',
|
||||||
|
'columnCount' => 'uint32',
|
||||||
|
'activeColumnCount' => 'uint32',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_trigger' => array(
|
||||||
|
'columns' => array('triggerPHID'),
|
||||||
|
'unique' => true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -13,6 +13,15 @@ final class PhabricatorProjectColumnStatusTransaction
|
||||||
$object->setStatus($value);
|
$object->setStatus($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function applyExternalEffects($object, $value) {
|
||||||
|
// Update the trigger usage index, which cares about whether columns are
|
||||||
|
// active or not.
|
||||||
|
$trigger_phid = $object->getTriggerPHID();
|
||||||
|
if ($trigger_phid) {
|
||||||
|
PhabricatorSearchWorker::queueDocumentForIndexing($trigger_phid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
$new = $this->getNewValue();
|
$new = $this->getNewValue();
|
||||||
|
|
||||||
|
|
|
@ -13,6 +13,25 @@ final class PhabricatorProjectColumnTriggerTransaction
|
||||||
$object->setTriggerPHID($value);
|
$object->setTriggerPHID($value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function applyExternalEffects($object, $value) {
|
||||||
|
// After we change the trigger attached to a column, update the search
|
||||||
|
// indexes for the old and new triggers so we update the usage index.
|
||||||
|
$old = $this->getOldValue();
|
||||||
|
$new = $this->getNewValue();
|
||||||
|
|
||||||
|
$column_phids = array();
|
||||||
|
if ($old) {
|
||||||
|
$column_phids[] = $old;
|
||||||
|
}
|
||||||
|
if ($new) {
|
||||||
|
$column_phids[] = $new;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($column_phids as $phid) {
|
||||||
|
PhabricatorSearchWorker::queueDocumentForIndexing($phid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
$old = $this->getOldValue();
|
$old = $this->getOldValue();
|
||||||
$new = $this->getNewValue();
|
$new = $this->getNewValue();
|
||||||
|
|
|
@ -136,7 +136,13 @@ final class PhabricatorSearchManagementIndexWorkflow
|
||||||
|
|
||||||
if ($track_skips) {
|
if ($track_skips) {
|
||||||
$new_versions = $this->loadIndexVersions($phid);
|
$new_versions = $this->loadIndexVersions($phid);
|
||||||
if ($old_versions !== $new_versions) {
|
|
||||||
|
if (!$old_versions && !$new_versions) {
|
||||||
|
// If the document doesn't use an index version, both the lists
|
||||||
|
// of versions will be empty. We still rebuild the index in this
|
||||||
|
// case.
|
||||||
|
$count_updated++;
|
||||||
|
} else if ($old_versions !== $new_versions) {
|
||||||
$count_updated++;
|
$count_updated++;
|
||||||
} else {
|
} else {
|
||||||
$count_skipped++;
|
$count_skipped++;
|
||||||
|
|
Loading…
Reference in a new issue