mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-30 10:42:41 +01:00
Merge branch 'master' into redesign-2015
This commit is contained in:
commit
f1b7fd483e
64 changed files with 813 additions and 242 deletions
17
resources/sql/autopatches/20150605.diviner.edges.sql
Normal file
17
resources/sql/autopatches/20150605.diviner.edges.sql
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_diviner.edge (
|
||||||
|
src VARBINARY(64) NOT NULL,
|
||||||
|
type INT UNSIGNED NOT NULL,
|
||||||
|
dst VARBINARY(64) NOT NULL,
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
seq INT UNSIGNED NOT NULL,
|
||||||
|
dataID INT UNSIGNED,
|
||||||
|
|
||||||
|
PRIMARY KEY (src, type, dst),
|
||||||
|
KEY src (src, type, dateCreated, seq),
|
||||||
|
UNIQUE KEY key_dst (dst, type, src)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
||||||
|
|
||||||
|
CREATE TABLE {$NAMESPACE}_diviner.edgedata (
|
||||||
|
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,6 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_diviner.diviner_livebook
|
||||||
|
ADD COLUMN editPolicy VARBINARY(64) NOT NULL AFTER viewPolicy;
|
||||||
|
|
||||||
|
UPDATE {$NAMESPACE}_diviner.diviner_livebook
|
||||||
|
SET editPolicy = 'admin'
|
||||||
|
WHERE editPolicy = '';
|
19
resources/sql/autopatches/20150605.diviner.xaction.sql
Normal file
19
resources/sql/autopatches/20150605.diviner.xaction.sql
Normal file
|
@ -0,0 +1,19 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_diviner.diviner_livebooktransaction (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
phid VARBINARY(64) NOT NULL,
|
||||||
|
authorPHID VARBINARY(64) NOT NULL,
|
||||||
|
objectPHID VARBINARY(64) NOT NULL,
|
||||||
|
viewPolicy VARBINARY(64) NOT NULL,
|
||||||
|
editPolicy VARBINARY(64) NOT NULL,
|
||||||
|
commentPHID VARBINARY(64) DEFAULT NULL,
|
||||||
|
commentVersion INT UNSIGNED NOT NULL,
|
||||||
|
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||||
|
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||||
|
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||||
|
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||||
|
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
|
||||||
|
dateCreated INT UNSIGNED NOT NULL,
|
||||||
|
dateModified INT UNSIGNED NOT NULL,
|
||||||
|
UNIQUE KEY key_phid (phid),
|
||||||
|
KEY key_object (objectPHID)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -648,19 +648,25 @@ phutil_register_library_map(array(
|
||||||
'DivinerAtomizeWorkflow' => 'applications/diviner/workflow/DivinerAtomizeWorkflow.php',
|
'DivinerAtomizeWorkflow' => 'applications/diviner/workflow/DivinerAtomizeWorkflow.php',
|
||||||
'DivinerAtomizer' => 'applications/diviner/atomizer/DivinerAtomizer.php',
|
'DivinerAtomizer' => 'applications/diviner/atomizer/DivinerAtomizer.php',
|
||||||
'DivinerBookController' => 'applications/diviner/controller/DivinerBookController.php',
|
'DivinerBookController' => 'applications/diviner/controller/DivinerBookController.php',
|
||||||
|
'DivinerBookEditController' => 'applications/diviner/controller/DivinerBookEditController.php',
|
||||||
'DivinerBookItemView' => 'applications/diviner/view/DivinerBookItemView.php',
|
'DivinerBookItemView' => 'applications/diviner/view/DivinerBookItemView.php',
|
||||||
'DivinerBookPHIDType' => 'applications/diviner/phid/DivinerBookPHIDType.php',
|
'DivinerBookPHIDType' => 'applications/diviner/phid/DivinerBookPHIDType.php',
|
||||||
'DivinerBookQuery' => 'applications/diviner/query/DivinerBookQuery.php',
|
'DivinerBookQuery' => 'applications/diviner/query/DivinerBookQuery.php',
|
||||||
'DivinerBookSearchIndexer' => 'applications/diviner/search/DivinerBookSearchIndexer.php',
|
'DivinerBookSearchIndexer' => 'applications/diviner/search/DivinerBookSearchIndexer.php',
|
||||||
'DivinerController' => 'applications/diviner/controller/DivinerController.php',
|
'DivinerController' => 'applications/diviner/controller/DivinerController.php',
|
||||||
'DivinerDAO' => 'applications/diviner/storage/DivinerDAO.php',
|
'DivinerDAO' => 'applications/diviner/storage/DivinerDAO.php',
|
||||||
|
'DivinerDefaultEditCapability' => 'applications/diviner/capability/DivinerDefaultEditCapability.php',
|
||||||
'DivinerDefaultRenderer' => 'applications/diviner/renderer/DivinerDefaultRenderer.php',
|
'DivinerDefaultRenderer' => 'applications/diviner/renderer/DivinerDefaultRenderer.php',
|
||||||
|
'DivinerDefaultViewCapability' => 'applications/diviner/capability/DivinerDefaultViewCapability.php',
|
||||||
'DivinerDiskCache' => 'applications/diviner/cache/DivinerDiskCache.php',
|
'DivinerDiskCache' => 'applications/diviner/cache/DivinerDiskCache.php',
|
||||||
'DivinerFileAtomizer' => 'applications/diviner/atomizer/DivinerFileAtomizer.php',
|
'DivinerFileAtomizer' => 'applications/diviner/atomizer/DivinerFileAtomizer.php',
|
||||||
'DivinerFindController' => 'applications/diviner/controller/DivinerFindController.php',
|
'DivinerFindController' => 'applications/diviner/controller/DivinerFindController.php',
|
||||||
'DivinerGenerateWorkflow' => 'applications/diviner/workflow/DivinerGenerateWorkflow.php',
|
'DivinerGenerateWorkflow' => 'applications/diviner/workflow/DivinerGenerateWorkflow.php',
|
||||||
'DivinerLiveAtom' => 'applications/diviner/storage/DivinerLiveAtom.php',
|
'DivinerLiveAtom' => 'applications/diviner/storage/DivinerLiveAtom.php',
|
||||||
'DivinerLiveBook' => 'applications/diviner/storage/DivinerLiveBook.php',
|
'DivinerLiveBook' => 'applications/diviner/storage/DivinerLiveBook.php',
|
||||||
|
'DivinerLiveBookEditor' => 'applications/diviner/editor/DivinerLiveBookEditor.php',
|
||||||
|
'DivinerLiveBookTransaction' => 'applications/diviner/storage/DivinerLiveBookTransaction.php',
|
||||||
|
'DivinerLiveBookTransactionQuery' => 'applications/diviner/query/DivinerLiveBookTransactionQuery.php',
|
||||||
'DivinerLivePublisher' => 'applications/diviner/publisher/DivinerLivePublisher.php',
|
'DivinerLivePublisher' => 'applications/diviner/publisher/DivinerLivePublisher.php',
|
||||||
'DivinerLiveSymbol' => 'applications/diviner/storage/DivinerLiveSymbol.php',
|
'DivinerLiveSymbol' => 'applications/diviner/storage/DivinerLiveSymbol.php',
|
||||||
'DivinerMainController' => 'applications/diviner/controller/DivinerMainController.php',
|
'DivinerMainController' => 'applications/diviner/controller/DivinerMainController.php',
|
||||||
|
@ -670,6 +676,7 @@ phutil_register_library_map(array(
|
||||||
'DivinerPublisher' => 'applications/diviner/publisher/DivinerPublisher.php',
|
'DivinerPublisher' => 'applications/diviner/publisher/DivinerPublisher.php',
|
||||||
'DivinerRenderer' => 'applications/diviner/renderer/DivinerRenderer.php',
|
'DivinerRenderer' => 'applications/diviner/renderer/DivinerRenderer.php',
|
||||||
'DivinerReturnTableView' => 'applications/diviner/view/DivinerReturnTableView.php',
|
'DivinerReturnTableView' => 'applications/diviner/view/DivinerReturnTableView.php',
|
||||||
|
'DivinerSchemaSpec' => 'applications/diviner/storage/DivinerSchemaSpec.php',
|
||||||
'DivinerSectionView' => 'applications/diviner/view/DivinerSectionView.php',
|
'DivinerSectionView' => 'applications/diviner/view/DivinerSectionView.php',
|
||||||
'DivinerStaticPublisher' => 'applications/diviner/publisher/DivinerStaticPublisher.php',
|
'DivinerStaticPublisher' => 'applications/diviner/publisher/DivinerStaticPublisher.php',
|
||||||
'DivinerSymbolRemarkupRule' => 'applications/diviner/markup/DivinerSymbolRemarkupRule.php',
|
'DivinerSymbolRemarkupRule' => 'applications/diviner/markup/DivinerSymbolRemarkupRule.php',
|
||||||
|
@ -1269,6 +1276,7 @@ phutil_register_library_map(array(
|
||||||
'PassphraseCredentialTypeTestCase' => 'applications/passphrase/credentialtype/__tests__/PassphraseCredentialTypeTestCase.php',
|
'PassphraseCredentialTypeTestCase' => 'applications/passphrase/credentialtype/__tests__/PassphraseCredentialTypeTestCase.php',
|
||||||
'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php',
|
'PassphraseCredentialViewController' => 'applications/passphrase/controller/PassphraseCredentialViewController.php',
|
||||||
'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php',
|
'PassphraseDAO' => 'applications/passphrase/storage/PassphraseDAO.php',
|
||||||
|
'PassphraseNoteCredentialType' => 'applications/passphrase/credentialtype/PassphraseNoteCredentialType.php',
|
||||||
'PassphrasePasswordCredentialType' => 'applications/passphrase/credentialtype/PassphrasePasswordCredentialType.php',
|
'PassphrasePasswordCredentialType' => 'applications/passphrase/credentialtype/PassphrasePasswordCredentialType.php',
|
||||||
'PassphrasePasswordKey' => 'applications/passphrase/keys/PassphrasePasswordKey.php',
|
'PassphrasePasswordKey' => 'applications/passphrase/keys/PassphrasePasswordKey.php',
|
||||||
'PassphraseQueryConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php',
|
'PassphraseQueryConduitAPIMethod' => 'applications/passphrase/conduit/PassphraseQueryConduitAPIMethod.php',
|
||||||
|
@ -4005,13 +4013,16 @@ phutil_register_library_map(array(
|
||||||
'DivinerAtomizeWorkflow' => 'DivinerWorkflow',
|
'DivinerAtomizeWorkflow' => 'DivinerWorkflow',
|
||||||
'DivinerAtomizer' => 'Phobject',
|
'DivinerAtomizer' => 'Phobject',
|
||||||
'DivinerBookController' => 'DivinerController',
|
'DivinerBookController' => 'DivinerController',
|
||||||
|
'DivinerBookEditController' => 'DivinerController',
|
||||||
'DivinerBookItemView' => 'AphrontTagView',
|
'DivinerBookItemView' => 'AphrontTagView',
|
||||||
'DivinerBookPHIDType' => 'PhabricatorPHIDType',
|
'DivinerBookPHIDType' => 'PhabricatorPHIDType',
|
||||||
'DivinerBookQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'DivinerBookQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
'DivinerBookSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
|
'DivinerBookSearchIndexer' => 'PhabricatorSearchDocumentIndexer',
|
||||||
'DivinerController' => 'PhabricatorController',
|
'DivinerController' => 'PhabricatorController',
|
||||||
'DivinerDAO' => 'PhabricatorLiskDAO',
|
'DivinerDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'DivinerDefaultEditCapability' => 'PhabricatorPolicyCapability',
|
||||||
'DivinerDefaultRenderer' => 'DivinerRenderer',
|
'DivinerDefaultRenderer' => 'DivinerRenderer',
|
||||||
|
'DivinerDefaultViewCapability' => 'PhabricatorPolicyCapability',
|
||||||
'DivinerDiskCache' => 'Phobject',
|
'DivinerDiskCache' => 'Phobject',
|
||||||
'DivinerFileAtomizer' => 'DivinerAtomizer',
|
'DivinerFileAtomizer' => 'DivinerAtomizer',
|
||||||
'DivinerFindController' => 'DivinerController',
|
'DivinerFindController' => 'DivinerController',
|
||||||
|
@ -4020,8 +4031,13 @@ phutil_register_library_map(array(
|
||||||
'DivinerLiveBook' => array(
|
'DivinerLiveBook' => array(
|
||||||
'DivinerDAO',
|
'DivinerDAO',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
|
'PhabricatorProjectInterface',
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
|
'PhabricatorApplicationTransactionInterface',
|
||||||
),
|
),
|
||||||
|
'DivinerLiveBookEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
'DivinerLiveBookTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
|
'DivinerLiveBookTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
'DivinerLivePublisher' => 'DivinerPublisher',
|
'DivinerLivePublisher' => 'DivinerPublisher',
|
||||||
'DivinerLiveSymbol' => array(
|
'DivinerLiveSymbol' => array(
|
||||||
'DivinerDAO',
|
'DivinerDAO',
|
||||||
|
@ -4036,6 +4052,7 @@ phutil_register_library_map(array(
|
||||||
'DivinerPublisher' => 'Phobject',
|
'DivinerPublisher' => 'Phobject',
|
||||||
'DivinerRenderer' => 'Phobject',
|
'DivinerRenderer' => 'Phobject',
|
||||||
'DivinerReturnTableView' => 'AphrontTagView',
|
'DivinerReturnTableView' => 'AphrontTagView',
|
||||||
|
'DivinerSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'DivinerSectionView' => 'AphrontTagView',
|
'DivinerSectionView' => 'AphrontTagView',
|
||||||
'DivinerStaticPublisher' => 'DivinerPublisher',
|
'DivinerStaticPublisher' => 'DivinerPublisher',
|
||||||
'DivinerSymbolRemarkupRule' => 'PhutilRemarkupRule',
|
'DivinerSymbolRemarkupRule' => 'PhutilRemarkupRule',
|
||||||
|
@ -4761,6 +4778,7 @@ phutil_register_library_map(array(
|
||||||
'PassphraseCredentialTypeTestCase' => 'PhabricatorTestCase',
|
'PassphraseCredentialTypeTestCase' => 'PhabricatorTestCase',
|
||||||
'PassphraseCredentialViewController' => 'PassphraseController',
|
'PassphraseCredentialViewController' => 'PassphraseController',
|
||||||
'PassphraseDAO' => 'PhabricatorLiskDAO',
|
'PassphraseDAO' => 'PhabricatorLiskDAO',
|
||||||
|
'PassphraseNoteCredentialType' => 'PassphraseCredentialType',
|
||||||
'PassphrasePasswordCredentialType' => 'PassphraseCredentialType',
|
'PassphrasePasswordCredentialType' => 'PassphraseCredentialType',
|
||||||
'PassphrasePasswordKey' => 'PassphraseAbstractKey',
|
'PassphrasePasswordKey' => 'PassphraseAbstractKey',
|
||||||
'PassphraseQueryConduitAPIMethod' => 'PassphraseConduitAPIMethod',
|
'PassphraseQueryConduitAPIMethod' => 'PassphraseConduitAPIMethod',
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class PhabricatorAuditActionConstants extends Phobject {
|
||||||
self::ACCEPT => pht("Accept Commit \xE2\x9C\x94"),
|
self::ACCEPT => pht("Accept Commit \xE2\x9C\x94"),
|
||||||
self::RESIGN => pht('Resign from Audit'),
|
self::RESIGN => pht('Resign from Audit'),
|
||||||
self::CLOSE => pht('Close Audit'),
|
self::CLOSE => pht('Close Audit'),
|
||||||
self::ADD_CCS => pht('Add CCs'),
|
self::ADD_CCS => pht('Add Subscribers'),
|
||||||
self::ADD_AUDITORS => pht('Add Auditors'),
|
self::ADD_AUDITORS => pht('Add Auditors'),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -449,16 +449,27 @@ abstract class PhabricatorApplication
|
||||||
$class,
|
$class,
|
||||||
PhabricatorUser $viewer) {
|
PhabricatorUser $viewer) {
|
||||||
|
|
||||||
if (!self::isClassInstalled($class)) {
|
$cache = PhabricatorCaches::getRequestCache();
|
||||||
return false;
|
$viewer_phid = $viewer->getPHID();
|
||||||
}
|
$key = 'app.'.$class.'.installed.'.$viewer_phid;
|
||||||
|
|
||||||
return PhabricatorPolicyFilter::hasCapability(
|
$result = $cache->getKey($key);
|
||||||
|
if ($result === null) {
|
||||||
|
if (!self::isClassInstalled($class)) {
|
||||||
|
$result = false;
|
||||||
|
} else {
|
||||||
|
$result = PhabricatorPolicyFilter::hasCapability(
|
||||||
$viewer,
|
$viewer,
|
||||||
self::getByClass($class),
|
self::getByClass($class),
|
||||||
PhabricatorPolicyCapability::CAN_VIEW);
|
PhabricatorPolicyCapability::CAN_VIEW);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$cache->setKey($key, $result);
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,16 @@ abstract class PhabricatorCalendarController extends PhabricatorController {
|
||||||
->setUser($this->getViewer())
|
->setUser($this->getViewer())
|
||||||
->addAction(
|
->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setName(pht('Create Private Event'))
|
->setName(pht('Create Event'))
|
||||||
->setHref('/calendar/event/create/?mode=private'))
|
->setHref('/calendar/event/create/'))
|
||||||
->addAction(
|
->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setName(pht('Create Public Event'))
|
->setName(pht('Create Public Event'))
|
||||||
->setHref('/calendar/event/create/?mode=public'));
|
->setHref('/calendar/event/create/?mode=public'))
|
||||||
|
->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('Create Recurring Event'))
|
||||||
|
->setHref('/calendar/event/create/?mode=recurring'));
|
||||||
|
|
||||||
$crumbs->addAction(
|
$crumbs->addAction(
|
||||||
id(new PHUIListItemView())
|
id(new PHUIListItemView())
|
||||||
|
|
|
@ -291,7 +291,9 @@ final class PhabricatorCalendarEventViewController
|
||||||
if ($event->getInstanceOfEventPHID()) {
|
if ($event->getInstanceOfEventPHID()) {
|
||||||
$properties->addProperty(
|
$properties->addProperty(
|
||||||
pht('Recurrence of Event'),
|
pht('Recurrence of Event'),
|
||||||
$viewer->renderHandle($event->getInstanceOfEventPHID()));
|
pht('%s of %s',
|
||||||
|
$event->getSequenceIndex(),
|
||||||
|
$viewer->renderHandle($event->getInstanceOfEventPHID())->render()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,9 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
$min_range = $this->getDateFrom($saved)->getEpoch();
|
$min_range = $this->getDateFrom($saved)->getEpoch();
|
||||||
$max_range = $this->getDateTo($saved)->getEpoch();
|
$max_range = $this->getDateTo($saved)->getEpoch();
|
||||||
|
|
||||||
|
$user_datasource = id(new PhabricatorPeopleUserFunctionDatasource())
|
||||||
|
->setViewer($viewer);
|
||||||
|
|
||||||
if ($this->isMonthView($saved) ||
|
if ($this->isMonthView($saved) ||
|
||||||
$this->isDayView($saved)) {
|
$this->isDayView($saved)) {
|
||||||
list($start_year, $start_month, $start_day) =
|
list($start_year, $start_month, $start_day) =
|
||||||
|
@ -124,11 +127,13 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
}
|
}
|
||||||
|
|
||||||
$invited_phids = $saved->getParameter('invitedPHIDs');
|
$invited_phids = $saved->getParameter('invitedPHIDs');
|
||||||
|
$invited_phids = $user_datasource->evaluateTokens($invited_phids);
|
||||||
if ($invited_phids) {
|
if ($invited_phids) {
|
||||||
$query->withInvitedPHIDs($invited_phids);
|
$query->withInvitedPHIDs($invited_phids);
|
||||||
}
|
}
|
||||||
|
|
||||||
$creator_phids = $saved->getParameter('creatorPHIDs');
|
$creator_phids = $saved->getParameter('creatorPHIDs');
|
||||||
|
$creator_phids = $user_datasource->evaluateTokens($creator_phids);
|
||||||
if ($creator_phids) {
|
if ($creator_phids) {
|
||||||
$query->withCreatorPHIDs($creator_phids);
|
$query->withCreatorPHIDs($creator_phids);
|
||||||
}
|
}
|
||||||
|
@ -196,13 +201,13 @@ final class PhabricatorCalendarEventSearchEngine
|
||||||
$form
|
$form
|
||||||
->appendControl(
|
->appendControl(
|
||||||
id(new AphrontFormTokenizerControl())
|
id(new AphrontFormTokenizerControl())
|
||||||
->setDatasource(new PhabricatorPeopleDatasource())
|
->setDatasource(new PhabricatorPeopleUserFunctionDatasource())
|
||||||
->setName('creators')
|
->setName('creators')
|
||||||
->setLabel(pht('Created By'))
|
->setLabel(pht('Created By'))
|
||||||
->setValue($creator_phids))
|
->setValue($creator_phids))
|
||||||
->appendControl(
|
->appendControl(
|
||||||
id(new AphrontFormTokenizerControl())
|
id(new AphrontFormTokenizerControl())
|
||||||
->setDatasource(new PhabricatorPeopleDatasource())
|
->setDatasource(new PhabricatorPeopleUserFunctionDatasource())
|
||||||
->setName('invited')
|
->setName('invited')
|
||||||
->setLabel(pht('Invited'))
|
->setLabel(pht('Invited'))
|
||||||
->setValue($invited_phids))
|
->setValue($invited_phids))
|
||||||
|
|
|
@ -50,10 +50,10 @@ final class PhabricatorCalendarEvent extends PhabricatorCalendarDAO
|
||||||
|
|
||||||
if ($mode == 'public') {
|
if ($mode == 'public') {
|
||||||
$view_policy = PhabricatorPolicies::getMostOpenPolicy();
|
$view_policy = PhabricatorPolicies::getMostOpenPolicy();
|
||||||
} else if ($mode == 'recurring') {
|
}
|
||||||
|
|
||||||
|
if ($mode == 'recurring') {
|
||||||
$is_recurring = true;
|
$is_recurring = true;
|
||||||
} else {
|
|
||||||
$view_policy = $actor->getPHID();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return id(new PhabricatorCalendarEvent())
|
return id(new PhabricatorCalendarEvent())
|
||||||
|
|
|
@ -27,14 +27,14 @@ final class PhabricatorAuthSetupCheck extends PhabricatorSetupCheck {
|
||||||
'You have not configured any authentication providers yet. You '.
|
'You have not configured any authentication providers yet. You '.
|
||||||
'should add a provider (like username/password, LDAP, or GitHub '.
|
'should add a provider (like username/password, LDAP, or GitHub '.
|
||||||
'OAuth) so users can register and log in. You can add and configure '.
|
'OAuth) so users can register and log in. You can add and configure '.
|
||||||
'providers using the [[%s | "Auth" application]].',
|
'providers using the Auth Application.');
|
||||||
'/auth/');
|
|
||||||
|
|
||||||
$this
|
$this
|
||||||
->newIssue('auth.noproviders')
|
->newIssue('auth.noproviders')
|
||||||
->setShortName(pht('No Auth Providers'))
|
->setShortName(pht('No Auth Providers'))
|
||||||
->setName(pht('No Authentication Providers Configured'))
|
->setName(pht('No Authentication Providers Configured'))
|
||||||
->setMessage($message);
|
->setMessage($message)
|
||||||
|
->addLink('/auth/', pht('Auth Application'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -355,6 +355,7 @@ final class ConpherenceThreadQuery
|
||||||
$start_epoch = $epochs['start_epoch'];
|
$start_epoch = $epochs['start_epoch'];
|
||||||
$end_epoch = $epochs['end_epoch'];
|
$end_epoch = $epochs['end_epoch'];
|
||||||
|
|
||||||
|
$events = array();
|
||||||
if ($participant_phids) {
|
if ($participant_phids) {
|
||||||
$events = id(new PhabricatorCalendarEventQuery())
|
$events = id(new PhabricatorCalendarEventQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
|
@ -363,8 +364,6 @@ final class ConpherenceThreadQuery
|
||||||
->withDateRange($start_epoch, $end_epoch)
|
->withDateRange($start_epoch, $end_epoch)
|
||||||
->execute();
|
->execute();
|
||||||
$events = mpull($events, null, 'getPHID');
|
$events = mpull($events, null, 'getPHID');
|
||||||
} else {
|
|
||||||
$events = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
$invitees = array();
|
$invitees = array();
|
||||||
|
|
|
@ -39,6 +39,7 @@ final class PhabricatorDivinerApplication extends PhabricatorApplication {
|
||||||
'find/' => 'DivinerFindController',
|
'find/' => 'DivinerFindController',
|
||||||
),
|
),
|
||||||
'/book/(?P<book>[^/]+)/' => 'DivinerBookController',
|
'/book/(?P<book>[^/]+)/' => 'DivinerBookController',
|
||||||
|
'/book/(?P<book>[^/]+)/edit/' => 'DivinerBookEditController',
|
||||||
'/book/'.
|
'/book/'.
|
||||||
'(?P<book>[^/]+)/'.
|
'(?P<book>[^/]+)/'.
|
||||||
'(?P<type>[^/]+)/'.
|
'(?P<type>[^/]+)/'.
|
||||||
|
@ -52,6 +53,18 @@ final class PhabricatorDivinerApplication extends PhabricatorApplication {
|
||||||
return self::GROUP_UTILITIES;
|
return self::GROUP_UTILITIES;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getCustomCapabilities() {
|
||||||
|
return array(
|
||||||
|
DivinerDefaultViewCapability::CAPABILITY => array(
|
||||||
|
'template' => DivinerBookPHIDType::TYPECONST,
|
||||||
|
),
|
||||||
|
DivinerDefaultEditCapability::CAPABILITY => array(
|
||||||
|
'default' => PhabricatorPolicies::POLICY_ADMIN,
|
||||||
|
'template' => DivinerBookPHIDType::TYPECONST,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
public function getRemarkupRules() {
|
public function getRemarkupRules() {
|
||||||
return array(
|
return array(
|
||||||
new DivinerSymbolRemarkupRule(),
|
new DivinerSymbolRemarkupRule(),
|
||||||
|
|
|
@ -20,7 +20,7 @@ final class DivinerArticleAtomizer extends DivinerAtomizer {
|
||||||
$atom->setDocblockMetaValue('title', $title);
|
$atom->setDocblockMetaValue('title', $title);
|
||||||
}
|
}
|
||||||
|
|
||||||
// If the article has no @name, use the filename after stripping any
|
// If the article has no `@name`, use the filename after stripping any
|
||||||
// extension.
|
// extension.
|
||||||
$name = idx($meta, 'name');
|
$name = idx($meta, 'name');
|
||||||
if (!$name) {
|
if (!$name) {
|
||||||
|
|
|
@ -151,9 +151,9 @@ final class DivinerPHPAtomizer extends DivinerAtomizer {
|
||||||
if (count($docs) < count($params)) {
|
if (count($docs) < count($params)) {
|
||||||
$atom->addWarning(
|
$atom->addWarning(
|
||||||
pht(
|
pht(
|
||||||
'This call takes %d parameters, but only %d are documented.',
|
'This call takes %s parameter(s), but only %s are documented.',
|
||||||
count($params),
|
new PhutilNumber(count($params)),
|
||||||
count($docs)));
|
new PhutilNumber(count($docs))));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -212,13 +212,13 @@ final class DivinerPHPAtomizer extends DivinerAtomizer {
|
||||||
if (preg_match('/@(return|param|task|author)/', $value, $matches)) {
|
if (preg_match('/@(return|param|task|author)/', $value, $matches)) {
|
||||||
$atom->addWarning(
|
$atom->addWarning(
|
||||||
pht(
|
pht(
|
||||||
'Atom "%s" is preceded by a comment containing "@%s", but the '.
|
'Atom "%s" is preceded by a comment containing `%s`, but '.
|
||||||
'comment is not a documentation comment. Documentation '.
|
'the comment is not a documentation comment. Documentation '.
|
||||||
'comments must begin with "%s", followed by a newline. Did '.
|
'comments must begin with `%s`, followed by a newline. Did '.
|
||||||
'you mean to use a documentation comment? (As the comment is '.
|
'you mean to use a documentation comment? (As the comment is '.
|
||||||
'not a documentation comment, it will be ignored.)',
|
'not a documentation comment, it will be ignored.)',
|
||||||
$atom->getName(),
|
$atom->getName(),
|
||||||
$matches[1],
|
'@'.$matches[1],
|
||||||
'/**'));
|
'/**'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -248,8 +248,8 @@ final class DivinerPHPAtomizer extends DivinerAtomizer {
|
||||||
if ($matches[1] !== $name) {
|
if ($matches[1] !== $name) {
|
||||||
$atom->addWarning(
|
$atom->addWarning(
|
||||||
pht(
|
pht(
|
||||||
'Parameter "%s" is named "%s" in the documentation. The '.
|
'Parameter "%s" is named "%s" in the documentation. '.
|
||||||
'documentation may be out of date.',
|
'The documentation may be out of date.',
|
||||||
$name,
|
$name,
|
||||||
$matches[1]));
|
$matches[1]));
|
||||||
}
|
}
|
||||||
|
@ -292,8 +292,8 @@ final class DivinerPHPAtomizer extends DivinerAtomizer {
|
||||||
if ($return) {
|
if ($return) {
|
||||||
$atom->addWarning(
|
$atom->addWarning(
|
||||||
pht(
|
pht(
|
||||||
'Method %s has explicitly documented %s. The %s method always '.
|
'Method `%s` has explicitly documented `%s`. The `%s` method '.
|
||||||
'returns %s. Diviner documents this implicitly.',
|
'always returns `%s`. Diviner documents this implicitly.',
|
||||||
'__construct()',
|
'__construct()',
|
||||||
'@return',
|
'@return',
|
||||||
'__construct()',
|
'__construct()',
|
||||||
|
|
|
@ -18,6 +18,7 @@ final class DivinerAtomCache extends DivinerDiskCache {
|
||||||
|
|
||||||
public function delete() {
|
public function delete() {
|
||||||
parent::delete();
|
parent::delete();
|
||||||
|
|
||||||
$this->fileHashMap = null;
|
$this->fileHashMap = null;
|
||||||
$this->atomMap = null;
|
$this->atomMap = null;
|
||||||
$this->atoms = array();
|
$this->atoms = array();
|
||||||
|
|
|
@ -26,8 +26,8 @@ abstract class DivinerDiskCache extends Phobject {
|
||||||
* Convert a long-form hash key like `ccbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaN` into
|
* Convert a long-form hash key like `ccbbaaaaaaaaaaaaaaaaaaaaaaaaaaaaN` into
|
||||||
* a shortened directory form, like `cc/bb/aaaaaaaaN`. In conjunction with
|
* a shortened directory form, like `cc/bb/aaaaaaaaN`. In conjunction with
|
||||||
* @{class:PhutilDirectoryKeyValueCache}, this gives us nice directories
|
* @{class:PhutilDirectoryKeyValueCache}, this gives us nice directories
|
||||||
* inside .divinercache instead of a million hash files with huge names at
|
* inside `.divinercache` instead of a million hash files with huge names at
|
||||||
* top level.
|
* the top level.
|
||||||
*/
|
*/
|
||||||
protected function getHashKey($hash) {
|
protected function getHashKey($hash) {
|
||||||
return implode(
|
return implode(
|
||||||
|
|
|
@ -45,6 +45,7 @@ final class DivinerPublishCache extends DivinerDiskCache {
|
||||||
|
|
||||||
/* -( Index )-------------------------------------------------------------- */
|
/* -( Index )-------------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
public function getIndex() {
|
public function getIndex() {
|
||||||
if ($this->index === null) {
|
if ($this->index === null) {
|
||||||
$this->index = $this->getCache()->getKey('index', array());
|
$this->index = $this->getCache()->getKey('index', array());
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerDefaultEditCapability extends PhabricatorPolicyCapability {
|
||||||
|
|
||||||
|
const CAPABILITY = 'diviner.default.edit';
|
||||||
|
|
||||||
|
public function getCapabilityName() {
|
||||||
|
return pht('Default Edit Policy');
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerDefaultViewCapability extends PhabricatorPolicyCapability {
|
||||||
|
|
||||||
|
const CAPABILITY = 'diviner.default.view';
|
||||||
|
|
||||||
|
public function getCapabilityName() {
|
||||||
|
return pht('Default View Policy');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldAllowPublicPolicySetting() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -2,33 +2,24 @@
|
||||||
|
|
||||||
final class DivinerAtomController extends DivinerController {
|
final class DivinerAtomController extends DivinerController {
|
||||||
|
|
||||||
private $bookName;
|
|
||||||
private $atomType;
|
|
||||||
private $atomName;
|
|
||||||
private $atomContext;
|
|
||||||
private $atomIndex;
|
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
public function shouldAllowPublic() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$this->bookName = $data['book'];
|
|
||||||
$this->atomType = $data['type'];
|
|
||||||
$this->atomName = $data['name'];
|
|
||||||
$this->atomContext = nonempty(idx($data, 'context'), null);
|
|
||||||
$this->atomIndex = nonempty(idx($data, 'index'), null);
|
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$viewer = $request->getUser();
|
$viewer = $request->getUser();
|
||||||
|
|
||||||
|
$book_name = $request->getURIData('book');
|
||||||
|
$atom_type = $request->getURIData('type');
|
||||||
|
$atom_name = $request->getURIData('name');
|
||||||
|
$atom_context = nonempty($request->getURIData('context'), null);
|
||||||
|
$atom_index = nonempty($request->getURIData('index'), null);
|
||||||
|
|
||||||
require_celerity_resource('diviner-shared-css');
|
require_celerity_resource('diviner-shared-css');
|
||||||
|
|
||||||
$book = id(new DivinerBookQuery())
|
$book = id(new DivinerBookQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withNames(array($this->bookName))
|
->withNames(array($book_name))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$book) {
|
if (!$book) {
|
||||||
|
@ -38,10 +29,10 @@ final class DivinerAtomController extends DivinerController {
|
||||||
$symbol = id(new DivinerAtomQuery())
|
$symbol = id(new DivinerAtomQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBookPHIDs(array($book->getPHID()))
|
->withBookPHIDs(array($book->getPHID()))
|
||||||
->withTypes(array($this->atomType))
|
->withTypes(array($atom_type))
|
||||||
->withNames(array($this->atomName))
|
->withNames(array($atom_name))
|
||||||
->withContexts(array($this->atomContext))
|
->withContexts(array($atom_context))
|
||||||
->withIndexes(array($this->atomIndex))
|
->withIndexes(array($atom_index))
|
||||||
->withIsDocumentable(true)
|
->withIsDocumentable(true)
|
||||||
->needAtoms(true)
|
->needAtoms(true)
|
||||||
->needExtends(true)
|
->needExtends(true)
|
||||||
|
@ -75,7 +66,7 @@ final class DivinerAtomController extends DivinerController {
|
||||||
->setName(DivinerAtom::getAtomTypeNameString(
|
->setName(DivinerAtom::getAtomTypeNameString(
|
||||||
$atom ? $atom->getType() : $symbol->getType())));
|
$atom ? $atom->getType() : $symbol->getType())));
|
||||||
|
|
||||||
$properties = id(new PHUIPropertyListView());
|
$properties = new PHUIPropertyListView();
|
||||||
|
|
||||||
$group = $atom ? $atom->getProperty('group') : $symbol->getGroupName();
|
$group = $atom ? $atom->getProperty('group') : $symbol->getGroupName();
|
||||||
if ($group) {
|
if ($group) {
|
||||||
|
@ -133,9 +124,7 @@ final class DivinerAtomController extends DivinerController {
|
||||||
$document->appendChild(
|
$document->appendChild(
|
||||||
id(new PHUIInfoView())
|
id(new PHUIInfoView())
|
||||||
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
|
||||||
->appendChild(
|
->appendChild(pht('This atom no longer exists.')));
|
||||||
pht(
|
|
||||||
'This atom no longer exists.')));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($atom) {
|
if ($atom) {
|
||||||
|
@ -303,7 +292,6 @@ final class DivinerAtomController extends DivinerController {
|
||||||
pht('Implements'),
|
pht('Implements'),
|
||||||
phutil_implode_html(phutil_tag('br'), $items));
|
phutil_implode_html(phutil_tag('br'), $items));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderAtomTag(DivinerLiveSymbol $symbol) {
|
private function renderAtomTag(DivinerLiveSymbol $symbol) {
|
||||||
|
@ -470,6 +458,7 @@ final class DivinerAtomController extends DivinerController {
|
||||||
private function renderFullSignature(
|
private function renderFullSignature(
|
||||||
DivinerLiveSymbol $symbol,
|
DivinerLiveSymbol $symbol,
|
||||||
$is_link = false) {
|
$is_link = false) {
|
||||||
|
|
||||||
switch ($symbol->getType()) {
|
switch ($symbol->getType()) {
|
||||||
case DivinerAtom::TYPE_CLASS:
|
case DivinerAtom::TYPE_CLASS:
|
||||||
case DivinerAtom::TYPE_INTERFACE:
|
case DivinerAtom::TYPE_INTERFACE:
|
||||||
|
|
|
@ -2,20 +2,15 @@
|
||||||
|
|
||||||
final class DivinerAtomListController extends DivinerController {
|
final class DivinerAtomListController extends DivinerController {
|
||||||
|
|
||||||
private $key;
|
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
public function shouldAllowPublic() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$this->key = idx($data, 'key', 'all');
|
$query_key = $request->getURIData('key');
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$controller = id(new PhabricatorApplicationSearchController())
|
$controller = id(new PhabricatorApplicationSearchController())
|
||||||
->setQueryKey($this->key)
|
->setQueryKey($query_key)
|
||||||
->setSearchEngine(new DivinerAtomSearchEngine())
|
->setSearchEngine(new DivinerAtomSearchEngine())
|
||||||
->setNavigation($this->buildSideNavView());
|
->setNavigation($this->buildSideNavView());
|
||||||
|
|
||||||
|
|
|
@ -2,41 +2,46 @@
|
||||||
|
|
||||||
final class DivinerBookController extends DivinerController {
|
final class DivinerBookController extends DivinerController {
|
||||||
|
|
||||||
private $bookName;
|
|
||||||
|
|
||||||
public function shouldAllowPublic() {
|
public function shouldAllowPublic() {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function willProcessRequest(array $data) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$this->bookName = $data['book'];
|
$viewer = $request->getViewer();
|
||||||
}
|
|
||||||
|
|
||||||
public function processRequest() {
|
$book_name = $request->getURIData('book');
|
||||||
$request = $this->getRequest();
|
|
||||||
$viewer = $request->getUser();
|
|
||||||
|
|
||||||
$book = id(new DivinerBookQuery())
|
$book = id(new DivinerBookQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withNames(array($this->bookName))
|
->withNames(array($book_name))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$book) {
|
if (!$book) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$actions = $this->buildActionView($viewer, $book);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->setBorder(true);
|
$crumbs->setBorder(true);
|
||||||
|
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
$book->getShortTitle(),
|
$book->getShortTitle(),
|
||||||
'/book/'.$book->getName().'/');
|
'/book/'.$book->getName().'/');
|
||||||
|
|
||||||
|
$action_button = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setText(pht('Actions'))
|
||||||
|
->setHref('#')
|
||||||
|
->setIconFont('fa-bars')
|
||||||
|
->addClass('phui-mobile-menu')
|
||||||
|
->setDropdownMenu($actions);
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($book->getTitle())
|
->setHeader($book->getTitle())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setPolicyObject($book)
|
->setPolicyObject($book)
|
||||||
->setEpoch($book->getDateModified());
|
->setEpoch($book->getDateModified())
|
||||||
|
->addActionLink($action_button);
|
||||||
|
|
||||||
$document = new PHUIDocumentView();
|
$document = new PHUIDocumentView();
|
||||||
$document->setHeader($header);
|
$document->setHeader($header);
|
||||||
|
@ -99,4 +104,28 @@ final class DivinerBookController extends DivinerController {
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildActionView(
|
||||||
|
PhabricatorUser $user,
|
||||||
|
DivinerLiveBook $book) {
|
||||||
|
|
||||||
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$user,
|
||||||
|
$book,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
|
$action_view = id(new PhabricatorActionListView())
|
||||||
|
->setUser($user)
|
||||||
|
->setObject($book)
|
||||||
|
->setObjectURI($this->getRequest()->getRequestURI());
|
||||||
|
|
||||||
|
$action_view->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setName(pht('Edit Book'))
|
||||||
|
->setIcon('fa-pencil')
|
||||||
|
->setHref('/book/'.$book->getName().'/edit/')
|
||||||
|
->setDisabled(!$can_edit));
|
||||||
|
|
||||||
|
return $action_view;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,117 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerBookEditController extends DivinerController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
|
$book_name = $request->getURIData('book');
|
||||||
|
|
||||||
|
$book = id(new DivinerBookQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->needProjectPHIDs(true)
|
||||||
|
->withNames(array($book_name))
|
||||||
|
->executeOne();
|
||||||
|
|
||||||
|
if (!$book) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$view_uri = '/book/'.$book->getName().'/';
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$v_projects = $request->getArr('projectPHIDs');
|
||||||
|
$v_view = $request->getStr('viewPolicy');
|
||||||
|
$v_edit = $request->getStr('editPolicy');
|
||||||
|
|
||||||
|
$xactions = array();
|
||||||
|
$xactions[] = id(new DivinerLiveBookTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||||
|
->setMetadataValue(
|
||||||
|
'edge:type',
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST)
|
||||||
|
->setNewValue(
|
||||||
|
array(
|
||||||
|
'=' => array_fuse($v_projects),
|
||||||
|
));
|
||||||
|
$xactions[] = id(new DivinerLiveBookTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
|
||||||
|
->setNewValue($v_view);
|
||||||
|
$xactions[] = id(new DivinerLiveBookTransaction())
|
||||||
|
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
|
||||||
|
->setNewValue($v_edit);
|
||||||
|
|
||||||
|
id(new DivinerLiveBookEditor())
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContentSourceFromRequest($request)
|
||||||
|
->setActor($viewer)
|
||||||
|
->applyTransactions($book, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($view_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$crumbs->addTextCrumb(pht('Edit Basics'));
|
||||||
|
|
||||||
|
$title = pht('Edit %s', $book->getTitle());
|
||||||
|
|
||||||
|
$policies = id(new PhabricatorPolicyQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setObject($book)
|
||||||
|
->execute();
|
||||||
|
$view_capability = PhabricatorPolicyCapability::CAN_VIEW;
|
||||||
|
$edit_capability = PhabricatorPolicyCapability::CAN_EDIT;
|
||||||
|
|
||||||
|
$form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->appendControl(
|
||||||
|
id(new AphrontFormTokenizerControl())
|
||||||
|
->setDatasource(new PhabricatorProjectDatasource())
|
||||||
|
->setName('projectPHIDs')
|
||||||
|
->setLabel(pht('Projects'))
|
||||||
|
->setValue($book->getProjectPHIDs()))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormPolicyControl())
|
||||||
|
->setName('viewPolicy')
|
||||||
|
->setPolicyObject($book)
|
||||||
|
->setCapability($view_capability)
|
||||||
|
->setPolicies($policies)
|
||||||
|
->setCaption($book->describeAutomaticCapability($view_capability)))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormPolicyControl())
|
||||||
|
->setName('editPolicy')
|
||||||
|
->setPolicyObject($book)
|
||||||
|
->setCapability($edit_capability)
|
||||||
|
->setPolicies($policies)
|
||||||
|
->setCaption($book->describeAutomaticCapability($edit_capability)))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->setValue(pht('Save'))
|
||||||
|
->addCancelButton($view_uri));
|
||||||
|
|
||||||
|
$object_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText($title)
|
||||||
|
->setForm($form);
|
||||||
|
|
||||||
|
$timeline = $this->buildTransactionTimeline(
|
||||||
|
$book,
|
||||||
|
new DivinerLiveBookTransactionQuery());
|
||||||
|
$timeline->setShouldTerminate(true);
|
||||||
|
|
||||||
|
return $this->buildApplicationPage(
|
||||||
|
array(
|
||||||
|
$crumbs,
|
||||||
|
$object_box,
|
||||||
|
$timeline,
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'title' => $title,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -3,19 +3,15 @@
|
||||||
abstract class DivinerController extends PhabricatorController {
|
abstract class DivinerController extends PhabricatorController {
|
||||||
|
|
||||||
protected function buildSideNavView() {
|
protected function buildSideNavView() {
|
||||||
$menu = $this->buildMenu();
|
$menu = $this->buildApplicationMenu();
|
||||||
return AphrontSideNavFilterView::newFromMenu($menu);
|
return AphrontSideNavFilterView::newFromMenu($menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function buildApplicationMenu() {
|
public function buildApplicationMenu() {
|
||||||
return $this->buildMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
private function buildMenu() {
|
|
||||||
$menu = new PHUIListView();
|
$menu = new PHUIListView();
|
||||||
|
|
||||||
id(new DivinerAtomSearchEngine())
|
id(new DivinerAtomSearchEngine())
|
||||||
->setViewer($this->getRequest()->getUser())
|
->setViewer($this->getRequest()->getViewer())
|
||||||
->addNavigationItems($menu);
|
->addNavigationItems($menu);
|
||||||
|
|
||||||
return $menu;
|
return $menu;
|
||||||
|
@ -24,12 +20,8 @@ abstract class DivinerController extends PhabricatorController {
|
||||||
protected function renderAtomList(array $symbols) {
|
protected function renderAtomList(array $symbols) {
|
||||||
assert_instances_of($symbols, 'DivinerLiveSymbol');
|
assert_instances_of($symbols, 'DivinerLiveSymbol');
|
||||||
|
|
||||||
$request = $this->getRequest();
|
|
||||||
$user = $request->getUser();
|
|
||||||
|
|
||||||
$list = array();
|
$list = array();
|
||||||
foreach ($symbols as $symbol) {
|
foreach ($symbols as $symbol) {
|
||||||
|
|
||||||
switch ($symbol->getType()) {
|
switch ($symbol->getType()) {
|
||||||
case DivinerAtom::TYPE_FUNCTION:
|
case DivinerAtom::TYPE_FUNCTION:
|
||||||
$title = $symbol->getTitle().'()';
|
$title = $symbol->getTitle().'()';
|
||||||
|
@ -43,8 +35,7 @@ abstract class DivinerController extends PhabricatorController {
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->setHref($symbol->getURI())
|
->setHref($symbol->getURI())
|
||||||
->setSubtitle($symbol->getSummary())
|
->setSubtitle($symbol->getSummary())
|
||||||
->setType(DivinerAtom::getAtomTypeNameString(
|
->setType(DivinerAtom::getAtomTypeNameString($symbol->getType()));
|
||||||
$symbol->getType()));
|
|
||||||
|
|
||||||
$list[] = $item;
|
$list[] = $item;
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,9 +6,8 @@ final class DivinerFindController extends DivinerController {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$request = $this->getRequest();
|
$viewer = $request->getViewer();
|
||||||
$viewer = $request->getUser();
|
|
||||||
|
|
||||||
$book_name = $request->getStr('book');
|
$book_name = $request->getStr('book');
|
||||||
$query_text = $request->getStr('name');
|
$query_text = $request->getStr('name');
|
||||||
|
@ -19,6 +18,7 @@ final class DivinerFindController extends DivinerController {
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withNames(array($book_name))
|
->withNames(array($book_name))
|
||||||
->executeOne();
|
->executeOne();
|
||||||
|
|
||||||
if (!$book) {
|
if (!$book) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
@ -70,8 +70,8 @@ final class DivinerFindController extends DivinerController {
|
||||||
->setTitle(pht('Documentation Not Found'))
|
->setTitle(pht('Documentation Not Found'))
|
||||||
->appendChild(
|
->appendChild(
|
||||||
pht(
|
pht(
|
||||||
'Unable to find the specified documentation. You may have '.
|
'Unable to find the specified documentation. '.
|
||||||
'followed a bad or outdated link.'))
|
'You may have followed a bad or outdated link.'))
|
||||||
->addCancelButton($not_found_uri, pht('Read More Documentation'));
|
->addCancelButton($not_found_uri, pht('Read More Documentation'));
|
||||||
|
|
||||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||||
|
|
|
@ -6,9 +6,8 @@ final class DivinerMainController extends DivinerController {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function processRequest() {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$request = $this->getRequest();
|
$viewer = $request->getViewer();
|
||||||
$viewer = $request->getUser();
|
|
||||||
|
|
||||||
$books = id(new DivinerBookQuery())
|
$books = id(new DivinerBookQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
|
@ -53,24 +52,20 @@ final class DivinerMainController extends DivinerController {
|
||||||
->appendChild($list);
|
->appendChild($list);
|
||||||
|
|
||||||
$document->appendChild($list);
|
$document->appendChild($list);
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
$text = pht(
|
$text = pht(
|
||||||
"(NOTE) **Looking for Phabricator documentation?** If you're looking ".
|
"(NOTE) **Looking for Phabricator documentation?** ".
|
||||||
"for help and information about Phabricator, you can ".
|
"If you're looking for help and information about Phabricator, ".
|
||||||
"[[ https://secure.phabricator.com/diviner/ | browse the public ".
|
"you can [[https://secure.phabricator.com/diviner/ | ".
|
||||||
"Phabricator documentation ]] on the live site.\n\n".
|
"browse the public Phabricator documentation]] on the live site.\n\n".
|
||||||
"Diviner is the documentation generator used to build the Phabricator ".
|
"Diviner is the documentation generator used to build the ".
|
||||||
"documentation.\n\n".
|
"Phabricator documentation.\n\n".
|
||||||
"You haven't generated any Diviner documentation books yet, so ".
|
"You haven't generated any Diviner documentation books yet, so ".
|
||||||
"there's nothing to show here. If you'd like to generate your own ".
|
"there's nothing to show here. If you'd like to generate your own ".
|
||||||
"local copy of the Phabricator documentation and have it appear ".
|
"local copy of the Phabricator documentation and have it appear ".
|
||||||
"here, run this command:\n\n".
|
"here, run this command:\n\n".
|
||||||
" phabricator/ $ ./bin/diviner generate\n\n".
|
" %s\n\n",
|
||||||
"Right now, Diviner isn't very useful for generating documentation ".
|
'phabricator/ $ ./bin/diviner generate');
|
||||||
"for projects other than Phabricator. If you're interested in using ".
|
|
||||||
"it in your own projects, leave feedback for us on ".
|
|
||||||
"[[ https://secure.phabricator.com/T4558 | T4558 ]].");
|
|
||||||
|
|
||||||
$text = PhabricatorMarkupEngine::renderOneObject(
|
$text = PhabricatorMarkupEngine::renderOneObject(
|
||||||
id(new PhabricatorMarkupOneOff())->setContent($text),
|
id(new PhabricatorMarkupOneOff())->setContent($text),
|
||||||
|
|
23
src/applications/diviner/editor/DivinerLiveBookEditor.php
Normal file
23
src/applications/diviner/editor/DivinerLiveBookEditor.php
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerLiveBookEditor
|
||||||
|
extends PhabricatorApplicationTransactionEditor {
|
||||||
|
|
||||||
|
public function getEditorApplicationClass() {
|
||||||
|
return 'PhabricatorDivinerApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getEditorObjectsDescription() {
|
||||||
|
return pht('Diviner Books');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionTypes() {
|
||||||
|
$types = parent::getTransactionTypes();
|
||||||
|
|
||||||
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
|
|
||||||
|
return $types;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,14 @@ final class DivinerAtomPHIDType extends PhabricatorPHIDType {
|
||||||
return new DivinerLiveSymbol();
|
return new DivinerLiveSymbol();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTypeIcon() {
|
||||||
|
return 'fa-cube';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHIDTypeApplicationClass() {
|
||||||
|
return 'PhabricatorDivinerApplication';
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildQueryForObjects(
|
protected function buildQueryForObjects(
|
||||||
PhabricatorObjectQuery $query,
|
PhabricatorObjectQuery $query,
|
||||||
array $phids) {
|
array $phids) {
|
||||||
|
|
|
@ -12,6 +12,14 @@ final class DivinerBookPHIDType extends PhabricatorPHIDType {
|
||||||
return new DivinerLiveBook();
|
return new DivinerLiveBook();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getTypeIcon() {
|
||||||
|
return 'fa-book';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPHIDTypeApplicationClass() {
|
||||||
|
return 'PhabricatorDivinerApplication';
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildQueryForObjects(
|
protected function buildQueryForObjects(
|
||||||
PhabricatorObjectQuery $query,
|
PhabricatorObjectQuery $query,
|
||||||
array $phids) {
|
array $phids) {
|
||||||
|
|
|
@ -8,11 +8,15 @@ final class DivinerLivePublisher extends DivinerPublisher {
|
||||||
if (!$this->book) {
|
if (!$this->book) {
|
||||||
$book_name = $this->getConfig('name');
|
$book_name = $this->getConfig('name');
|
||||||
|
|
||||||
$book = id(new DivinerLiveBook())->loadOneWhere('name = %s', $book_name);
|
$book = id(new DivinerLiveBook())->loadOneWhere(
|
||||||
|
'name = %s',
|
||||||
|
$book_name);
|
||||||
|
|
||||||
if (!$book) {
|
if (!$book) {
|
||||||
$book = id(new DivinerLiveBook())
|
$book = id(new DivinerLiveBook())
|
||||||
->setName($book_name)
|
->setName($book_name)
|
||||||
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
|
->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy())
|
||||||
|
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||||
->save();
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -144,7 +148,6 @@ final class DivinerLivePublisher extends DivinerPublisher {
|
||||||
->setContent(null)
|
->setContent(null)
|
||||||
->save();
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -133,10 +133,20 @@ abstract class DivinerPublisher extends Phobject {
|
||||||
$created = array_keys($created);
|
$created = array_keys($created);
|
||||||
}
|
}
|
||||||
|
|
||||||
echo pht('Deleting %d documents.', count($deleted))."\n";
|
$console = PhutilConsole::getConsole();
|
||||||
|
|
||||||
|
$console->writeOut(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Deleting %s document(s).',
|
||||||
|
new PhutilNumber(count($deleted))));
|
||||||
$this->deleteDocumentsByHash($deleted);
|
$this->deleteDocumentsByHash($deleted);
|
||||||
|
|
||||||
echo pht('Creating %d documents.', count($created))."\n";
|
$console->writeOut(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Creating %s document(s).',
|
||||||
|
new PhutilNumber(count($created))));
|
||||||
$this->createDocumentsByHash($created);
|
$this->createDocumentsByHash($created);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,7 +79,6 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Include or exclude "ghosts", which are symbols which used to exist but do
|
* Include or exclude "ghosts", which are symbols which used to exist but do
|
||||||
* not exist currently (for example, a function which existed in an older
|
* not exist currently (for example, a function which existed in an older
|
||||||
|
@ -137,6 +136,7 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
foreach ($atoms as $key => $atom) {
|
foreach ($atoms as $key => $atom) {
|
||||||
$book = idx($books, $atom->getBookPHID());
|
$book = idx($books, $atom->getBookPHID());
|
||||||
if (!$book) {
|
if (!$book) {
|
||||||
|
$this->didRejectResult($atom);
|
||||||
unset($atoms[$key]);
|
unset($atoms[$key]);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
@ -158,12 +158,9 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
// Load all of the symbols this symbol extends, recursively. Commonly,
|
// Load all of the symbols this symbol extends, recursively. Commonly,
|
||||||
// this means all the ancestor classes and interfaces it extends and
|
// this means all the ancestor classes and interfaces it extends and
|
||||||
// implements.
|
// implements.
|
||||||
|
|
||||||
if ($this->needExtends) {
|
if ($this->needExtends) {
|
||||||
|
|
||||||
// First, load all the matching symbols by name. This does 99% of the
|
// First, load all the matching symbols by name. This does 99% of the
|
||||||
// work in most cases, assuming things are named at all reasonably.
|
// work in most cases, assuming things are named at all reasonably.
|
||||||
|
|
||||||
$names = array();
|
$names = array();
|
||||||
foreach ($atoms as $atom) {
|
foreach ($atoms as $atom) {
|
||||||
if (!$atom->getAtom()) {
|
if (!$atom->getAtom()) {
|
||||||
|
@ -303,6 +300,7 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
if ($this->titles) {
|
if ($this->titles) {
|
||||||
$hashes = array();
|
$hashes = array();
|
||||||
|
|
||||||
foreach ($this->titles as $title) {
|
foreach ($this->titles as $title) {
|
||||||
$slug = DivinerAtomRef::normalizeTitleString($title);
|
$slug = DivinerAtomRef::normalizeTitleString($title);
|
||||||
$hash = PhabricatorHash::digestForIndex($slug);
|
$hash = PhabricatorHash::digestForIndex($slug);
|
||||||
|
@ -318,6 +316,7 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
if ($this->contexts) {
|
if ($this->contexts) {
|
||||||
$with_null = false;
|
$with_null = false;
|
||||||
$contexts = $this->contexts;
|
$contexts = $this->contexts;
|
||||||
|
|
||||||
foreach ($contexts as $key => $value) {
|
foreach ($contexts as $key => $value) {
|
||||||
if ($value === null) {
|
if ($value === null) {
|
||||||
unset($contexts[$key]);
|
unset($contexts[$key]);
|
||||||
|
@ -373,10 +372,9 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->nameContains) {
|
if ($this->nameContains) {
|
||||||
// NOTE: This CONVERT() call makes queries case-insensitive, since the
|
// NOTE: This `CONVERT()` call makes queries case-insensitive, since
|
||||||
// column has binary collation. Eventually, this should move into
|
// the column has binary collation. Eventually, this should move into
|
||||||
// fulltext.
|
// fulltext.
|
||||||
|
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn_r,
|
$conn_r,
|
||||||
'CONVERT(name USING utf8) LIKE %~',
|
'CONVERT(name USING utf8) LIKE %~',
|
||||||
|
@ -388,7 +386,6 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this->formatWhereClause($where);
|
return $this->formatWhereClause($where);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Walk a list of atoms and collect all the node hashes of the atoms'
|
* Walk a list of atoms and collect all the node hashes of the atoms'
|
||||||
* children. When recursing, also walk up the tree and collect children of
|
* children. When recursing, also walk up the tree and collect children of
|
||||||
|
@ -413,6 +410,7 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
foreach ($child_hashes as $hash) {
|
foreach ($child_hashes as $hash) {
|
||||||
$hashes[$hash] = $hash;
|
$hashes[$hash] = $hash;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($recurse_up) {
|
if ($recurse_up) {
|
||||||
$hashes += $this->getAllChildHashes($symbol->getExtends(), true);
|
$hashes += $this->getAllChildHashes($symbol->getExtends(), true);
|
||||||
}
|
}
|
||||||
|
@ -421,7 +419,6 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $hashes;
|
return $hashes;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Attach child atoms to existing atoms. In recursive mode, also attach child
|
* Attach child atoms to existing atoms. In recursive mode, also attach child
|
||||||
* atoms to atoms that these atoms extend.
|
* atoms to atoms that these atoms extend.
|
||||||
|
@ -452,7 +449,9 @@ final class DivinerAtomQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
$symbol_children[] = $children[$hash];
|
$symbol_children[] = $children[$hash];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$symbol->attachChildren($symbol_children);
|
$symbol->attachChildren($symbol_children);
|
||||||
|
|
||||||
if ($recurse_up) {
|
if ($recurse_up) {
|
||||||
$this->attachAllChildren($symbol->getExtends(), $children, true);
|
$this->attachAllChildren($symbol->getExtends(), $children, true);
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,8 @@ final class DivinerBookQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
private $phids;
|
private $phids;
|
||||||
private $names;
|
private $names;
|
||||||
|
|
||||||
|
private $needProjectPHIDs;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
return $this;
|
return $this;
|
||||||
|
@ -21,6 +23,11 @@ final class DivinerBookQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needProjectPHIDs($need_phids) {
|
||||||
|
$this->needProjectPHIDs = $need_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
protected function loadPage() {
|
protected function loadPage() {
|
||||||
$table = new DivinerLiveBook();
|
$table = new DivinerLiveBook();
|
||||||
$conn_r = $table->establishConnection('r');
|
$conn_r = $table->establishConnection('r');
|
||||||
|
@ -36,6 +43,30 @@ final class DivinerBookQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $table->loadAllFromArray($data);
|
return $table->loadAllFromArray($data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function didFilterPage(array $books) {
|
||||||
|
assert_instances_of($books, 'DivinerLiveBook');
|
||||||
|
|
||||||
|
if ($this->needProjectPHIDs) {
|
||||||
|
$edge_query = id(new PhabricatorEdgeQuery())
|
||||||
|
->withSourcePHIDs(mpull($books, 'getPHID'))
|
||||||
|
->withEdgeTypes(
|
||||||
|
array(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
));
|
||||||
|
$edge_query->execute();
|
||||||
|
|
||||||
|
foreach ($books as $book) {
|
||||||
|
$project_phids = $edge_query->getDestinationPHIDs(
|
||||||
|
array(
|
||||||
|
$book->getPHID(),
|
||||||
|
));
|
||||||
|
$book->attachProjectPHIDs($project_phids);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $books;
|
||||||
|
}
|
||||||
|
|
||||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||||
$where = array();
|
$where = array();
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerLiveBookTransactionQuery
|
||||||
|
extends PhabricatorApplicationTransactionQuery {
|
||||||
|
|
||||||
|
public function getTemplateApplicationTransaction() {
|
||||||
|
return new DivinerLiveBookTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -202,8 +202,8 @@ final class DivinerDefaultRenderer extends DivinerRenderer {
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($ref->getBook() != $this->getConfig('name')) {
|
if ($ref->getBook() != $this->getConfig('name')) {
|
||||||
// If the ref is from a different book, we can't normalize it. Just return
|
// If the ref is from a different book, we can't normalize it.
|
||||||
// it as-is if it has enough information to resolve.
|
// Just return it as-is if it has enough information to resolve.
|
||||||
if ($ref->getName() && $ref->getType()) {
|
if ($ref->getName() && $ref->getType()) {
|
||||||
return $ref;
|
return $ref;
|
||||||
} else {
|
} else {
|
||||||
|
@ -260,5 +260,4 @@ final class DivinerDefaultRenderer extends DivinerRenderer {
|
||||||
$ref->getTitle());
|
$ref->getTitle());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,7 +27,15 @@ final class DivinerAtomSearchIndexer extends PhabricatorSearchDocumentIndexer {
|
||||||
PhabricatorSearchRelationship::RELATIONSHIP_BOOK,
|
PhabricatorSearchRelationship::RELATIONSHIP_BOOK,
|
||||||
$atom->getBookPHID(),
|
$atom->getBookPHID(),
|
||||||
DivinerBookPHIDType::TYPECONST,
|
DivinerBookPHIDType::TYPECONST,
|
||||||
$book->getDateCreated());
|
PhabricatorTime::getNow());
|
||||||
|
|
||||||
|
$doc->addRelationship(
|
||||||
|
$atom->getGraphHash()
|
||||||
|
? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
|
||||||
|
: PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
|
||||||
|
$atom->getBookPHID(),
|
||||||
|
DivinerBookPHIDType::TYPECONST,
|
||||||
|
PhabricatorTime::getNow());
|
||||||
|
|
||||||
return $doc;
|
return $doc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,6 +18,11 @@ final class DivinerBookSearchIndexer extends PhabricatorSearchDocumentIndexer {
|
||||||
PhabricatorSearchDocumentFieldType::FIELD_BODY,
|
PhabricatorSearchDocumentFieldType::FIELD_BODY,
|
||||||
$book->getPreface());
|
$book->getPreface());
|
||||||
|
|
||||||
|
$this->indexTransactions(
|
||||||
|
$doc,
|
||||||
|
new DivinerLiveBookTransactionQuery(),
|
||||||
|
array($phid));
|
||||||
|
|
||||||
return $doc;
|
return $doc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -3,12 +3,17 @@
|
||||||
final class DivinerLiveBook extends DivinerDAO
|
final class DivinerLiveBook extends DivinerDAO
|
||||||
implements
|
implements
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
PhabricatorDestructibleInterface {
|
PhabricatorProjectInterface,
|
||||||
|
PhabricatorDestructibleInterface,
|
||||||
|
PhabricatorApplicationTransactionInterface {
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $viewPolicy;
|
protected $viewPolicy;
|
||||||
|
protected $editPolicy;
|
||||||
protected $configurationData = array();
|
protected $configurationData = array();
|
||||||
|
|
||||||
|
private $projectPHIDs = self::ATTACHABLE;
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
@ -63,16 +68,33 @@ final class DivinerLiveBook extends DivinerDAO
|
||||||
return idx($spec, 'name', $group);
|
return idx($spec, 'name', $group);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function attachProjectPHIDs(array $project_phids) {
|
||||||
|
$this->projectPHIDs = $project_phids;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProjectPHIDs() {
|
||||||
|
return $this->assertAttached($this->projectPHIDs);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
public function getCapabilities() {
|
public function getCapabilities() {
|
||||||
return array(
|
return array(
|
||||||
PhabricatorPolicyCapability::CAN_VIEW,
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getPolicy($capability) {
|
public function getPolicy($capability) {
|
||||||
return PhabricatorPolicies::getMostOpenPolicy();
|
switch ($capability) {
|
||||||
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||||
|
return $this->getViewPolicy();
|
||||||
|
case PhabricatorPolicyCapability::CAN_EDIT:
|
||||||
|
return $this->getEditPolicy();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
@ -83,8 +105,10 @@ final class DivinerLiveBook extends DivinerDAO
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
public function destroyObjectPermanently(
|
public function destroyObjectPermanently(
|
||||||
PhabricatorDestructionEngine $engine) {
|
PhabricatorDestructionEngine $engine) {
|
||||||
|
|
||||||
|
@ -102,4 +126,27 @@ final class DivinerLiveBook extends DivinerDAO
|
||||||
$this->saveTransaction();
|
$this->saveTransaction();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getApplicationTransactionEditor() {
|
||||||
|
return new DivinerLiveBookEditor();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionObject() {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionTemplate() {
|
||||||
|
return new DivinerLiveBookTransaction();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willRenderTimeline(
|
||||||
|
PhabricatorApplicationTransactionView $timeline,
|
||||||
|
AphrontRequest $request) {
|
||||||
|
|
||||||
|
return $timeline;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,18 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerLiveBookTransaction
|
||||||
|
extends PhabricatorApplicationTransaction {
|
||||||
|
|
||||||
|
public function getApplicationName() {
|
||||||
|
return 'diviner';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionType() {
|
||||||
|
return DivinerBookPHIDType::TYPECONST;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getApplicationTransactionCommentObject() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -137,10 +137,9 @@ final class DivinerLiveSymbol extends DivinerDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function save() {
|
public function save() {
|
||||||
|
|
||||||
// NOTE: The identity hash is just a sanity check because the unique tuple
|
// NOTE: The identity hash is just a sanity check because the unique tuple
|
||||||
// on this table is way way too long to fit into a normal UNIQUE KEY. We
|
// on this table is way way too long to fit into a normal `UNIQUE KEY`.
|
||||||
// don't use it directly, but its existence prevents duplicate records.
|
// We don't use it directly, but its existence prevents duplicate records.
|
||||||
|
|
||||||
if (!$this->identityHash) {
|
if (!$this->identityHash) {
|
||||||
$this->identityHash = PhabricatorHash::digestForIndex(
|
$this->identityHash = PhabricatorHash::digestForIndex(
|
||||||
|
@ -159,14 +158,17 @@ final class DivinerLiveSymbol extends DivinerDAO
|
||||||
|
|
||||||
public function getTitle() {
|
public function getTitle() {
|
||||||
$title = parent::getTitle();
|
$title = parent::getTitle();
|
||||||
|
|
||||||
if (!strlen($title)) {
|
if (!strlen($title)) {
|
||||||
$title = $this->getName();
|
$title = $this->getName();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $title;
|
return $title;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function setTitle($value) {
|
public function setTitle($value) {
|
||||||
$this->writeField('title', $value);
|
$this->writeField('title', $value);
|
||||||
|
|
||||||
if (strlen($value)) {
|
if (strlen($value)) {
|
||||||
$slug = DivinerAtomRef::normalizeTitleString($value);
|
$slug = DivinerAtomRef::normalizeTitleString($value);
|
||||||
$hash = PhabricatorHash::digestForIndex($slug);
|
$hash = PhabricatorHash::digestForIndex($slug);
|
||||||
|
@ -174,6 +176,7 @@ final class DivinerLiveSymbol extends DivinerDAO
|
||||||
} else {
|
} else {
|
||||||
$this->titleSlugHash = null;
|
$this->titleSlugHash = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -200,16 +203,15 @@ final class DivinerLiveSymbol extends DivinerDAO
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
public function getCapabilities() {
|
public function getCapabilities() {
|
||||||
return $this->getBook()->getCapabilities();
|
return $this->getBook()->getCapabilities();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getPolicy($capability) {
|
public function getPolicy($capability) {
|
||||||
return $this->getBook()->getPolicy($capability);
|
return $this->getBook()->getPolicy($capability);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
return $this->getBook()->hasAutomaticCapability($capability, $viewer);
|
return $this->getBook()->hasAutomaticCapability($capability, $viewer);
|
||||||
}
|
}
|
||||||
|
@ -219,19 +221,17 @@ final class DivinerLiveSymbol extends DivinerDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Markup Interface )--------------------------------------------------- */
|
/* -( PhabricatorMarkupInterface )------------------------------------------ */
|
||||||
|
|
||||||
|
|
||||||
public function getMarkupFieldKey($field) {
|
public function getMarkupFieldKey($field) {
|
||||||
return $this->getPHID().':'.$field.':'.$this->getGraphHash();
|
return $this->getPHID().':'.$field.':'.$this->getGraphHash();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function newMarkupEngine($field) {
|
public function newMarkupEngine($field) {
|
||||||
return PhabricatorMarkupEngine::getEngine('diviner');
|
return PhabricatorMarkupEngine::getEngine('diviner');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function getMarkupText($field) {
|
public function getMarkupText($field) {
|
||||||
if (!$this->getAtom()) {
|
if (!$this->getAtom()) {
|
||||||
return;
|
return;
|
||||||
|
@ -240,21 +240,18 @@ final class DivinerLiveSymbol extends DivinerDAO
|
||||||
return $this->getAtom()->getDocblockText();
|
return $this->getAtom()->getDocblockText();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function didMarkupText($field, $output, PhutilMarkupEngine $engine) {
|
||||||
public function didMarkupText(
|
|
||||||
$field,
|
|
||||||
$output,
|
|
||||||
PhutilMarkupEngine $engine) {
|
|
||||||
return $output;
|
return $output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public function shouldUseMarkupCache($field) {
|
public function shouldUseMarkupCache($field) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
/* -( PhabricatorDestructibleInterface )----------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
public function destroyObjectPermanently(
|
public function destroyObjectPermanently(
|
||||||
PhabricatorDestructionEngine $engine) {
|
PhabricatorDestructionEngine $engine) {
|
||||||
|
|
||||||
|
|
9
src/applications/diviner/storage/DivinerSchemaSpec.php
Normal file
9
src/applications/diviner/storage/DivinerSchemaSpec.php
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DivinerSchemaSpec extends PhabricatorConfigSchemaSpec {
|
||||||
|
|
||||||
|
public function buildSchemata() {
|
||||||
|
$this->buildEdgeSchemata(new DivinerLiveBook());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -36,8 +36,10 @@ final class DivinerAtomizeWorkflow extends DivinerWorkflow {
|
||||||
|
|
||||||
$atomizer_class = $args->getArg('atomizer');
|
$atomizer_class = $args->getArg('atomizer');
|
||||||
if (!$atomizer_class) {
|
if (!$atomizer_class) {
|
||||||
throw new Exception(
|
throw new PhutilArgumentUsageException(
|
||||||
pht('Specify an atomizer class with %s.', '--atomizer'));
|
pht(
|
||||||
|
'Specify an atomizer class with %s.',
|
||||||
|
'--atomizer'));
|
||||||
}
|
}
|
||||||
|
|
||||||
$symbols = id(new PhutilSymbolLoader())
|
$symbols = id(new PhutilSymbolLoader())
|
||||||
|
@ -46,7 +48,7 @@ final class DivinerAtomizeWorkflow extends DivinerWorkflow {
|
||||||
->setAncestorClass('DivinerAtomizer')
|
->setAncestorClass('DivinerAtomizer')
|
||||||
->selectAndLoadSymbols();
|
->selectAndLoadSymbols();
|
||||||
if (!$symbols) {
|
if (!$symbols) {
|
||||||
throw new Exception(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Atomizer class '%s' must be a concrete subclass of %s.",
|
"Atomizer class '%s' must be a concrete subclass of %s.",
|
||||||
$atomizer_class,
|
$atomizer_class,
|
||||||
|
|
|
@ -50,6 +50,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
} else {
|
} else {
|
||||||
$cwd = getcwd();
|
$cwd = getcwd();
|
||||||
$this->log(pht('FINDING DOCUMENTATION BOOKS'));
|
$this->log(pht('FINDING DOCUMENTATION BOOKS'));
|
||||||
|
|
||||||
$books = id(new FileFinder($cwd))
|
$books = id(new FileFinder($cwd))
|
||||||
->withType('f')
|
->withType('f')
|
||||||
->withSuffix('book')
|
->withSuffix('book')
|
||||||
|
@ -92,7 +93,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
// amount of work we can, so that regenerating documentation after minor
|
// amount of work we can, so that regenerating documentation after minor
|
||||||
// changes is quick.
|
// changes is quick.
|
||||||
//
|
//
|
||||||
// = ATOM CACHE =
|
// = Atom Cache =
|
||||||
//
|
//
|
||||||
// In the first stage, we find all the direct changes to source code since
|
// In the first stage, we find all the direct changes to source code since
|
||||||
// the last run. This stage relies on two data structures:
|
// the last run. This stage relies on two data structures:
|
||||||
|
@ -118,7 +119,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
// its methods). The File Hash Map contains an exhaustive list of all atoms
|
// its methods). The File Hash Map contains an exhaustive list of all atoms
|
||||||
// with type "file", but not child atoms of those top-level atoms.)
|
// with type "file", but not child atoms of those top-level atoms.)
|
||||||
//
|
//
|
||||||
// = GRAPH CACHE =
|
// = Graph Cache =
|
||||||
//
|
//
|
||||||
// We now know which atoms exist, and can compare the Atom Map to some
|
// We now know which atoms exist, and can compare the Atom Map to some
|
||||||
// existing cache to figure out what has changed. However, this isn't
|
// existing cache to figure out what has changed. However, this isn't
|
||||||
|
@ -176,8 +177,9 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
->setConcreteOnly(true)
|
->setConcreteOnly(true)
|
||||||
->setAncestorClass('DivinerPublisher')
|
->setAncestorClass('DivinerPublisher')
|
||||||
->selectAndLoadSymbols();
|
->selectAndLoadSymbols();
|
||||||
|
|
||||||
if (!$symbols) {
|
if (!$symbols) {
|
||||||
throw new Exception(
|
throw new PhutilArgumentUsageException(
|
||||||
pht(
|
pht(
|
||||||
"Publisher class '%s' must be a concrete subclass of %s.",
|
"Publisher class '%s' must be a concrete subclass of %s.",
|
||||||
$publisher_class,
|
$publisher_class,
|
||||||
|
@ -188,22 +190,37 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
$this->publishDocumentation($args->getArg('clean'), $publisher);
|
$this->publishDocumentation($args->getArg('clean'), $publisher);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Atom Cache )--------------------------------------------------------- */
|
/* -( Atom Cache )--------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
private function buildAtomCache() {
|
private function buildAtomCache() {
|
||||||
$this->log(pht('BUILDING ATOM CACHE'));
|
$this->log(pht('BUILDING ATOM CACHE'));
|
||||||
|
|
||||||
$file_hashes = $this->findFilesInProject();
|
$file_hashes = $this->findFilesInProject();
|
||||||
$this->log(pht('Found %d file(s) in project.', count($file_hashes)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Found %s file(s) in project.',
|
||||||
|
new PhutilNumber(count($file_hashes))));
|
||||||
|
|
||||||
$this->deleteDeadAtoms($file_hashes);
|
$this->deleteDeadAtoms($file_hashes);
|
||||||
$atomize = $this->getFilesToAtomize($file_hashes);
|
$atomize = $this->getFilesToAtomize($file_hashes);
|
||||||
$this->log(pht('Found %d unatomized, uncached file(s).', count($atomize)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Found %s unatomized, uncached file(s).',
|
||||||
|
new PhutilNumber(count($atomize))));
|
||||||
|
|
||||||
$file_atomizers = $this->getAtomizersForFiles($atomize);
|
$file_atomizers = $this->getAtomizersForFiles($atomize);
|
||||||
$this->log(pht('Found %d file(s) to atomize.', count($file_atomizers)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Found %s file(s) to atomize.',
|
||||||
|
new PhutilNumber(count($file_atomizers))));
|
||||||
|
|
||||||
$futures = $this->buildAtomizerFutures($file_atomizers);
|
$futures = $this->buildAtomizerFutures($file_atomizers);
|
||||||
$this->log(pht('Atomizing %d file(s).', count($file_atomizers)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Atomizing %s file(s).',
|
||||||
|
new PhutilNumber(count($file_atomizers))));
|
||||||
|
|
||||||
if ($futures) {
|
if ($futures) {
|
||||||
$this->resolveAtomizerFutures($futures, $file_hashes);
|
$this->resolveAtomizerFutures($futures, $file_hashes);
|
||||||
|
@ -344,6 +361,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
->setTotal(count($futures));
|
->setTotal(count($futures));
|
||||||
$futures = id(new FutureIterator($futures))
|
$futures = id(new FutureIterator($futures))
|
||||||
->limit(4);
|
->limit(4);
|
||||||
|
|
||||||
foreach ($futures as $key => $future) {
|
foreach ($futures as $key => $future) {
|
||||||
try {
|
try {
|
||||||
$atoms = $future->resolveJSON();
|
$atoms = $future->resolveJSON();
|
||||||
|
@ -396,6 +414,7 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
|
|
||||||
/* -( Graph Cache )-------------------------------------------------------- */
|
/* -( Graph Cache )-------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
private function buildGraphCache() {
|
private function buildGraphCache() {
|
||||||
$this->log(pht('BUILDING GRAPH CACHE'));
|
$this->log(pht('BUILDING GRAPH CACHE'));
|
||||||
|
|
||||||
|
@ -407,7 +426,10 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
$dirty_nhashes = array();
|
$dirty_nhashes = array();
|
||||||
|
|
||||||
$del_atoms = array_diff_key($symbol_map, $atoms);
|
$del_atoms = array_diff_key($symbol_map, $atoms);
|
||||||
$this->log(pht('Found %d obsolete atom(s) in graph.', count($del_atoms)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Found %s obsolete atom(s) in graph.',
|
||||||
|
new PhutilNumber(count($del_atoms))));
|
||||||
|
|
||||||
foreach ($del_atoms as $nhash => $shash) {
|
foreach ($del_atoms as $nhash => $shash) {
|
||||||
$atom_cache->deleteSymbol($nhash);
|
$atom_cache->deleteSymbol($nhash);
|
||||||
|
@ -418,7 +440,10 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
}
|
}
|
||||||
|
|
||||||
$new_atoms = array_diff_key($atoms, $symbol_map);
|
$new_atoms = array_diff_key($atoms, $symbol_map);
|
||||||
$this->log(pht('Found %d new atom(s) in graph.', count($new_atoms)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Found %s new atom(s) in graph.',
|
||||||
|
new PhutilNumber(count($new_atoms))));
|
||||||
|
|
||||||
foreach ($new_atoms as $nhash => $ignored) {
|
foreach ($new_atoms as $nhash => $ignored) {
|
||||||
$shash = $this->computeSymbolHash($nhash);
|
$shash = $this->computeSymbolHash($nhash);
|
||||||
|
@ -454,7 +479,10 @@ final class DivinerGenerateWorkflow extends DivinerWorkflow {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->log(pht('Found %d affected atoms.', count($dirty_nhashes)));
|
$this->log(
|
||||||
|
pht(
|
||||||
|
'Found %s affected atoms.',
|
||||||
|
new PhutilNumber(count($dirty_nhashes))));
|
||||||
|
|
||||||
foreach ($dirty_nhashes as $nhash => $ignored) {
|
foreach ($dirty_nhashes as $nhash => $ignored) {
|
||||||
$atom_cache->addGraph($nhash, $this->computeGraphHash($nhash));
|
$atom_cache->addGraph($nhash, $this->computeGraphHash($nhash));
|
||||||
|
|
|
@ -850,8 +850,8 @@ abstract class HeraldAdapter extends Phobject {
|
||||||
case HeraldRuleTypeConfig::RULE_TYPE_OBJECT:
|
case HeraldRuleTypeConfig::RULE_TYPE_OBJECT:
|
||||||
$standard = array(
|
$standard = array(
|
||||||
self::ACTION_NOTHING => pht('Do nothing'),
|
self::ACTION_NOTHING => pht('Do nothing'),
|
||||||
self::ACTION_ADD_CC => pht('Add emails to CC'),
|
self::ACTION_ADD_CC => pht('Add Subscribers'),
|
||||||
self::ACTION_REMOVE_CC => pht('Remove emails from CC'),
|
self::ACTION_REMOVE_CC => pht('Remove Subscribers'),
|
||||||
self::ACTION_EMAIL => pht('Send an email to'),
|
self::ACTION_EMAIL => pht('Send an email to'),
|
||||||
self::ACTION_AUDIT => pht('Trigger an Audit by'),
|
self::ACTION_AUDIT => pht('Trigger an Audit by'),
|
||||||
self::ACTION_FLAG => pht('Mark with flag'),
|
self::ACTION_FLAG => pht('Mark with flag'),
|
||||||
|
@ -868,8 +868,8 @@ abstract class HeraldAdapter extends Phobject {
|
||||||
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
case HeraldRuleTypeConfig::RULE_TYPE_PERSONAL:
|
||||||
$standard = array(
|
$standard = array(
|
||||||
self::ACTION_NOTHING => pht('Do nothing'),
|
self::ACTION_NOTHING => pht('Do nothing'),
|
||||||
self::ACTION_ADD_CC => pht('Add me to CC'),
|
self::ACTION_ADD_CC => pht('Add me as a subscriber'),
|
||||||
self::ACTION_REMOVE_CC => pht('Remove me from CC'),
|
self::ACTION_REMOVE_CC => pht('Remove me as a subscriber'),
|
||||||
self::ACTION_EMAIL => pht('Send me an email'),
|
self::ACTION_EMAIL => pht('Send me an email'),
|
||||||
self::ACTION_AUDIT => pht('Trigger an Audit by me'),
|
self::ACTION_AUDIT => pht('Trigger an Audit by me'),
|
||||||
self::ACTION_FLAG => pht('Mark with flag'),
|
self::ACTION_FLAG => pht('Mark with flag'),
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
final class PhabricatorMailImplementationPHPMailerAdapter
|
final class PhabricatorMailImplementationPHPMailerAdapter
|
||||||
extends PhabricatorMailImplementationAdapter {
|
extends PhabricatorMailImplementationAdapter {
|
||||||
|
|
||||||
|
private $mailer;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @phutil-external-symbol class PHPMailer
|
* @phutil-external-symbol class PHPMailer
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -55,7 +55,7 @@ final class PhabricatorMailTarget extends Phobject {
|
||||||
return $this->viewer;
|
return $this->viewer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function sendMail(PhabricatorMetaMTAMail $mail) {
|
public function willSendMail(PhabricatorMetaMTAMail $mail) {
|
||||||
$viewer = $this->getViewer();
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
$mail->addPHIDHeaders('X-Phabricator-To', $this->rawToPHIDs);
|
$mail->addPHIDHeaders('X-Phabricator-To', $this->rawToPHIDs);
|
||||||
|
@ -92,7 +92,7 @@ final class PhabricatorMailTarget extends Phobject {
|
||||||
$mail->addCCs($cc);
|
$mail->addCCs($cc);
|
||||||
}
|
}
|
||||||
|
|
||||||
return $mail->save();
|
return $mail;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getRecipientsSummary(
|
private function getRecipientsSummary(
|
||||||
|
|
|
@ -47,7 +47,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
|
||||||
$is_new = true;
|
$is_new = true;
|
||||||
|
|
||||||
// Prefill username if provided.
|
// Prefill username if provided.
|
||||||
$credential->setUsername($request->getStr('username'));
|
$credential->setUsername((string)$request->getStr('username'));
|
||||||
|
|
||||||
if (!$request->getStr('isInitialized')) {
|
if (!$request->getStr('isInitialized')) {
|
||||||
$type->didInitializeNewCredential($viewer, $credential);
|
$type->didInitializeNewCredential($viewer, $credential);
|
||||||
|
@ -151,10 +151,11 @@ final class PassphraseCredentialEditController extends PassphraseController {
|
||||||
$credential->openTransaction();
|
$credential->openTransaction();
|
||||||
|
|
||||||
if (!$credential->getIsLocked()) {
|
if (!$credential->getIsLocked()) {
|
||||||
|
if ($type->shouldRequireUsername()) {
|
||||||
$xactions[] = id(new PassphraseCredentialTransaction())
|
$xactions[] = id(new PassphraseCredentialTransaction())
|
||||||
->setTransactionType($type_username)
|
->setTransactionType($type_username)
|
||||||
->setNewValue($v_username);
|
->setNewValue($v_username);
|
||||||
|
}
|
||||||
// If some value other than a sequence of bullets was provided for
|
// If some value other than a sequence of bullets was provided for
|
||||||
// the credential, update it. In particular, note that we are
|
// the credential, update it. In particular, note that we are
|
||||||
// explicitly allowing empty secrets: one use case is HTTP auth where
|
// explicitly allowing empty secrets: one use case is HTTP auth where
|
||||||
|
@ -263,6 +264,7 @@ final class PassphraseCredentialEditController extends PassphraseController {
|
||||||
pht('This credential is permanently locked and can not be edited.'));
|
pht('This credential is permanently locked and can not be edited.'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($type->shouldRequireUsername()) {
|
||||||
$form
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
id(new AphrontFormTextControl())
|
id(new AphrontFormTextControl())
|
||||||
|
@ -270,7 +272,9 @@ final class PassphraseCredentialEditController extends PassphraseController {
|
||||||
->setLabel(pht('Login/Username'))
|
->setLabel(pht('Login/Username'))
|
||||||
->setValue($v_username)
|
->setValue($v_username)
|
||||||
->setDisabled($credential_is_locked)
|
->setDisabled($credential_is_locked)
|
||||||
->setError($e_username))
|
->setError($e_username));
|
||||||
|
}
|
||||||
|
$form
|
||||||
->appendChild(
|
->appendChild(
|
||||||
$secret_control
|
$secret_control
|
||||||
->setName('secret')
|
->setName('secret')
|
||||||
|
|
|
@ -182,9 +182,11 @@ final class PassphraseCredentialViewController extends PassphraseController {
|
||||||
pht('Editable By'),
|
pht('Editable By'),
|
||||||
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
|
$descriptions[PhabricatorPolicyCapability::CAN_EDIT]);
|
||||||
|
|
||||||
|
if ($type->shouldRequireUsername()) {
|
||||||
$properties->addProperty(
|
$properties->addProperty(
|
||||||
pht('Username'),
|
pht('Username'),
|
||||||
$credential->getUsername());
|
$credential->getUsername());
|
||||||
|
}
|
||||||
|
|
||||||
$used_by_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
$used_by_phids = PhabricatorEdgeQuery::loadDestinationPHIDs(
|
||||||
$credential->getPHID(),
|
$credential->getPHID(),
|
||||||
|
|
|
@ -131,4 +131,8 @@ abstract class PassphraseCredentialType extends Phobject {
|
||||||
return $secret;
|
return $secret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldRequireUsername() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PassphraseNoteCredentialType
|
||||||
|
extends PassphraseCredentialType {
|
||||||
|
|
||||||
|
const CREDENTIAL_TYPE = 'note';
|
||||||
|
const PROVIDES_TYPE = 'provides/note';
|
||||||
|
|
||||||
|
public function getCredentialType() {
|
||||||
|
return self::CREDENTIAL_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProvidesType() {
|
||||||
|
return self::PROVIDES_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCredentialTypeName() {
|
||||||
|
return pht('Note');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCredentialTypeDescription() {
|
||||||
|
return pht('Store a plaintext note.');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getSecretLabel() {
|
||||||
|
return pht('Note');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newSecretControl() {
|
||||||
|
return id(new AphrontFormTextAreaControl());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldRequireUsername() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -174,6 +174,10 @@ final class PassphraseCredentialTransactionEditor
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case PassphraseCredentialTransaction::TYPE_USERNAME:
|
case PassphraseCredentialTransaction::TYPE_USERNAME:
|
||||||
|
$credential_type = $object->getCredentialTypeImplementation();
|
||||||
|
if (!$credential_type->shouldRequireUsername()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
$missing = $this->validateIsEmptyTextField(
|
$missing = $this->validateIsEmptyTextField(
|
||||||
$object->getUsername(),
|
$object->getUsername(),
|
||||||
$xactions);
|
$xactions);
|
||||||
|
|
|
@ -36,13 +36,14 @@ final class PhrictionMoveController extends PhrictionController {
|
||||||
// about it.
|
// about it.
|
||||||
if (strlen($v_slug)) {
|
if (strlen($v_slug)) {
|
||||||
$normal_slug = PhabricatorSlug::normalize($v_slug);
|
$normal_slug = PhabricatorSlug::normalize($v_slug);
|
||||||
if ($normal_slug !== $v_slug) {
|
$no_slash_slug = rtrim($normal_slug, '/');
|
||||||
|
if ($normal_slug !== $v_slug && $no_slash_slug !== $v_slug) {
|
||||||
return $this->newDialog()
|
return $this->newDialog()
|
||||||
->setTitle(pht('Adjust Path'))
|
->setTitle(pht('Adjust Path'))
|
||||||
->appendParagraph(
|
->appendParagraph(
|
||||||
pht(
|
pht(
|
||||||
'The path you entered (%s) is not a valid wiki document '.
|
'The path you entered (%s) is not a valid wiki document '.
|
||||||
'path. Paths may not contain special characters.',
|
'path. Paths may not contain spaces or special characters.',
|
||||||
phutil_tag('strong', array(), $v_slug)))
|
phutil_tag('strong', array(), $v_slug)))
|
||||||
->appendParagraph(
|
->appendParagraph(
|
||||||
pht(
|
pht(
|
||||||
|
|
|
@ -391,6 +391,10 @@ final class PhrictionTransactionEditor
|
||||||
pht("A document's content changes."),
|
pht("A document's content changes."),
|
||||||
PhrictionTransaction::MAILTAG_DELETE =>
|
PhrictionTransaction::MAILTAG_DELETE =>
|
||||||
pht('A document is deleted.'),
|
pht('A document is deleted.'),
|
||||||
|
PhrictionTransaction::MAILTAG_SUBSCRIBERS =>
|
||||||
|
pht('A document\'s subscribers change.'),
|
||||||
|
PhrictionTransaction::MAILTAG_OTHER =>
|
||||||
|
pht('Other document activity not listed above occurs.'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -12,6 +12,8 @@ final class PhrictionTransaction
|
||||||
const MAILTAG_TITLE = 'phriction-title';
|
const MAILTAG_TITLE = 'phriction-title';
|
||||||
const MAILTAG_CONTENT = 'phriction-content';
|
const MAILTAG_CONTENT = 'phriction-content';
|
||||||
const MAILTAG_DELETE = 'phriction-delete';
|
const MAILTAG_DELETE = 'phriction-delete';
|
||||||
|
const MAILTAG_SUBSCRIBERS = 'phriction-subscribers';
|
||||||
|
const MAILTAG_OTHER = 'phriction-other';
|
||||||
|
|
||||||
public function getApplicationName() {
|
public function getApplicationName() {
|
||||||
return 'phriction';
|
return 'phriction';
|
||||||
|
@ -280,7 +282,12 @@ final class PhrictionTransaction
|
||||||
case self::TYPE_DELETE:
|
case self::TYPE_DELETE:
|
||||||
$tags[] = self::MAILTAG_DELETE;
|
$tags[] = self::MAILTAG_DELETE;
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||||
|
$tags[] = self::MAILTAG_SUBSCRIBERS;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
$tags[] = self::MAILTAG_OTHER;
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
return $tags;
|
return $tags;
|
||||||
}
|
}
|
||||||
|
|
|
@ -433,6 +433,8 @@ final class PhabricatorProjectTransactionEditor
|
||||||
pht('Project membership changes.'),
|
pht('Project membership changes.'),
|
||||||
PhabricatorProjectTransaction::MAILTAG_WATCHERS =>
|
PhabricatorProjectTransaction::MAILTAG_WATCHERS =>
|
||||||
pht('Project watcher list changes.'),
|
pht('Project watcher list changes.'),
|
||||||
|
PhabricatorProjectTransaction::MAILTAG_SUBSCRIBERS =>
|
||||||
|
pht('Project subscribers change.'),
|
||||||
PhabricatorProjectTransaction::MAILTAG_OTHER =>
|
PhabricatorProjectTransaction::MAILTAG_OTHER =>
|
||||||
pht('Other project activity not listed above occurs.'),
|
pht('Other project activity not listed above occurs.'),
|
||||||
);
|
);
|
||||||
|
|
|
@ -16,6 +16,7 @@ final class PhabricatorProjectTransaction
|
||||||
|
|
||||||
const MAILTAG_METADATA = 'project-metadata';
|
const MAILTAG_METADATA = 'project-metadata';
|
||||||
const MAILTAG_MEMBERS = 'project-members';
|
const MAILTAG_MEMBERS = 'project-members';
|
||||||
|
const MAILTAG_SUBSCRIBERS = 'project-subscribers';
|
||||||
const MAILTAG_WATCHERS = 'project-watchers';
|
const MAILTAG_WATCHERS = 'project-watchers';
|
||||||
const MAILTAG_OTHER = 'project-other';
|
const MAILTAG_OTHER = 'project-other';
|
||||||
|
|
||||||
|
@ -369,6 +370,9 @@ final class PhabricatorProjectTransaction
|
||||||
case self::TYPE_COLOR:
|
case self::TYPE_COLOR:
|
||||||
$tags[] = self::MAILTAG_METADATA;
|
$tags[] = self::MAILTAG_METADATA;
|
||||||
break;
|
break;
|
||||||
|
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||||
|
$tags[] = self::MAILTAG_SUBSCRIBERS;
|
||||||
|
break;
|
||||||
case PhabricatorTransactions::TYPE_EDGE:
|
case PhabricatorTransactions::TYPE_EDGE:
|
||||||
$type = $this->getMetadata('edge:type');
|
$type = $this->getMetadata('edge:type');
|
||||||
$type = head($type);
|
$type = head($type);
|
||||||
|
|
|
@ -166,28 +166,22 @@ final class ReleephRequestTransactionalEditor
|
||||||
protected function shouldSendMail(
|
protected function shouldSendMail(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function sendMail(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
array $xactions) {
|
|
||||||
|
|
||||||
// Avoid sending emails that only talk about commit discovery.
|
// Avoid sending emails that only talk about commit discovery.
|
||||||
$types = array_unique(mpull($xactions, 'getTransactionType'));
|
$types = array_unique(mpull($xactions, 'getTransactionType'));
|
||||||
if ($types === array(ReleephRequestTransaction::TYPE_DISCOVERY)) {
|
if ($types === array(ReleephRequestTransaction::TYPE_DISCOVERY)) {
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Don't email people when we discover that something picks or reverts OK.
|
// Don't email people when we discover that something picks or reverts OK.
|
||||||
if ($types === array(ReleephRequestTransaction::TYPE_PICK_STATUS)) {
|
if ($types === array(ReleephRequestTransaction::TYPE_PICK_STATUS)) {
|
||||||
if (!mfilter($xactions, 'isBoringPickStatus', true /* negate */)) {
|
if (!mfilter($xactions, 'isBoringPickStatus', true /* negate */)) {
|
||||||
// If we effectively call "isInterestingPickStatus" and get nothing...
|
// If we effectively call "isInterestingPickStatus" and get nothing...
|
||||||
return null;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::sendMail($object, $xactions);
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||||
|
|
|
@ -29,18 +29,25 @@ final class PhabricatorCommitBranchesField
|
||||||
'callsign' => $this->getObject()->getRepository()->getCallsign(),
|
'callsign' => $this->getObject()->getRepository()->getCallsign(),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
try {
|
||||||
$branches_raw = id(new ConduitCall('diffusion.branchquery', $params))
|
$branches_raw = id(new ConduitCall('diffusion.branchquery', $params))
|
||||||
->setUser($this->getViewer())
|
->setUser($this->getViewer())
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$branches = DiffusionRepositoryRef::loadAllFromDictionaries($branches_raw);
|
$branches = DiffusionRepositoryRef::loadAllFromDictionaries(
|
||||||
|
$branches_raw);
|
||||||
if (!$branches) {
|
if (!$branches) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$branch_names = mpull($branches, 'getShortName');
|
$branch_names = mpull($branches, 'getShortName');
|
||||||
sort($branch_names);
|
sort($branch_names);
|
||||||
|
$branch_text = implode(', ', $branch_names);
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$branch_text = pht('<%s: %s>', get_class($ex), $ex->getMessage());
|
||||||
|
}
|
||||||
|
|
||||||
$body->addTextSection(pht('BRANCHES'), implode(', ', $branch_names));
|
$body->addTextSection(pht('BRANCHES'), $branch_text);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1040,10 +1040,10 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
// Hook for edges or other properties that may need (re-)loading
|
// Hook for edges or other properties that may need (re-)loading
|
||||||
$object = $this->willPublish($object, $xactions);
|
$object = $this->willPublish($object, $xactions);
|
||||||
|
|
||||||
$mailed = array();
|
$messages = array();
|
||||||
if (!$this->getDisableEmail()) {
|
if (!$this->getDisableEmail()) {
|
||||||
if ($this->shouldSendMail($object, $xactions)) {
|
if ($this->shouldSendMail($object, $xactions)) {
|
||||||
$mailed = $this->sendMail($object, $xactions);
|
$messages = $this->buildMail($object, $xactions);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1055,10 +1055,21 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->shouldPublishFeedStory($object, $xactions)) {
|
if ($this->shouldPublishFeedStory($object, $xactions)) {
|
||||||
$this->publishFeedStory(
|
$mailed = array();
|
||||||
$object,
|
foreach ($messages as $mail) {
|
||||||
$xactions,
|
foreach ($mail->buildRecipientList() as $phid) {
|
||||||
$mailed);
|
$mailed[$phid] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->publishFeedStory($object, $xactions, $mailed);
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: This actually sends the mail. We do this last to reduce the chance
|
||||||
|
// that we send some mail, hit an exception, then send the mail again when
|
||||||
|
// retrying.
|
||||||
|
foreach ($messages as $mail) {
|
||||||
|
$mail->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
return $xactions;
|
return $xactions;
|
||||||
|
@ -2241,7 +2252,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
/**
|
/**
|
||||||
* @task mail
|
* @task mail
|
||||||
*/
|
*/
|
||||||
protected function sendMail(
|
private function buildMail(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
|
|
||||||
|
@ -2255,8 +2266,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
// Set this explicitly before we start swapping out the effective actor.
|
// Set this explicitly before we start swapping out the effective actor.
|
||||||
$this->setActingAsPHID($this->getActingAsPHID());
|
$this->setActingAsPHID($this->getActingAsPHID());
|
||||||
|
|
||||||
|
$messages = array();
|
||||||
$mailed = array();
|
|
||||||
foreach ($targets as $target) {
|
foreach ($targets as $target) {
|
||||||
$original_actor = $this->getActor();
|
$original_actor = $this->getActor();
|
||||||
|
|
||||||
|
@ -2270,7 +2280,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
// Reload handles for the new viewer.
|
// Reload handles for the new viewer.
|
||||||
$this->loadHandles($xactions);
|
$this->loadHandles($xactions);
|
||||||
|
|
||||||
$mail = $this->sendMailToTarget($object, $xactions, $target);
|
$mail = $this->buildMailForTarget($object, $xactions, $target);
|
||||||
} catch (Exception $ex) {
|
} catch (Exception $ex) {
|
||||||
$caught = $ex;
|
$caught = $ex;
|
||||||
}
|
}
|
||||||
|
@ -2283,16 +2293,14 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($mail) {
|
if ($mail) {
|
||||||
foreach ($mail->buildRecipientList() as $phid) {
|
$messages[] = $mail;
|
||||||
$mailed[$phid] = true;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_keys($mailed);
|
return $messages;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function sendMailToTarget(
|
private function buildMailForTarget(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions,
|
array $xactions,
|
||||||
PhabricatorMailTarget $target) {
|
PhabricatorMailTarget $target) {
|
||||||
|
@ -2354,7 +2362,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
||||||
$mail->setParentMessageID($this->getParentMessageID());
|
$mail->setParentMessageID($this->getParentMessageID());
|
||||||
}
|
}
|
||||||
|
|
||||||
return $target->sendMail($mail);
|
return $target->willSendMail($mail);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function addMailProjectMetadata(
|
private function addMailProjectMetadata(
|
||||||
|
|
|
@ -1124,6 +1124,52 @@ final class PhabricatorUSEnglishTranslation
|
||||||
'%s changed package owners, added: %4$s; removed: %6$s.',
|
'%s changed package owners, added: %4$s; removed: %6$s.',
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'Found %s book(s).' => array(
|
||||||
|
'Found %s book.',
|
||||||
|
'Found %s books.',
|
||||||
|
),
|
||||||
|
'Found %s file(s) in project.' => array(
|
||||||
|
'Found %s file in project.',
|
||||||
|
'Found %s files in project.',
|
||||||
|
),
|
||||||
|
'Found %s unatomized, uncached file(s).' => array(
|
||||||
|
'Found %s unatomized, uncached file.',
|
||||||
|
'Found %s unatomized, uncached files.',
|
||||||
|
),
|
||||||
|
'Found %s file(s) to atomize.' => array(
|
||||||
|
'Found %s file to atomize.',
|
||||||
|
'Found %s files to atomize.',
|
||||||
|
),
|
||||||
|
'Atomizing %s file(s).' => array(
|
||||||
|
'Atomizing %s file.',
|
||||||
|
'Atomizing %s files.',
|
||||||
|
),
|
||||||
|
'Creating %s document(s).' => array(
|
||||||
|
'Creating %s document.',
|
||||||
|
'Creating %s documents.',
|
||||||
|
),
|
||||||
|
'Deleting %s document(s).' => array(
|
||||||
|
'Deleting %s document.',
|
||||||
|
'Deleting %s documents.',
|
||||||
|
),
|
||||||
|
'Found %s obsolete atom(s) in graph.' => array(
|
||||||
|
'Found %s obsolete atom in graph.',
|
||||||
|
'Found %s obsolete atoms in graph.',
|
||||||
|
),
|
||||||
|
'Found %s new atom(s) in graph.' => array(
|
||||||
|
'Found %s new atom in graph.',
|
||||||
|
'Found %s new atoms in graph.',
|
||||||
|
),
|
||||||
|
'This call takes %s parameter(s), but only %s are documented.' => array(
|
||||||
|
array(
|
||||||
|
'This call takes %s parameter, but only %s is documented.',
|
||||||
|
'This call takes %s parameter, but only %s are documented.',
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'This call takes %s parameters, but only %s is documented.',
|
||||||
|
'This call takes %s parameters, but only %s are documented.',
|
||||||
|
),
|
||||||
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -872,6 +872,15 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
* @task order
|
* @task order
|
||||||
*/
|
*/
|
||||||
public function getOrderableColumns() {
|
public function getOrderableColumns() {
|
||||||
|
$cache = PhabricatorCaches::getRequestCache();
|
||||||
|
$class = get_class($this);
|
||||||
|
$cache_key = 'query.orderablecolumns.'.$class;
|
||||||
|
|
||||||
|
$columns = $cache->getKey($cache_key);
|
||||||
|
if ($columns !== null) {
|
||||||
|
return $columns;
|
||||||
|
}
|
||||||
|
|
||||||
$columns = array(
|
$columns = array(
|
||||||
'id' => array(
|
'id' => array(
|
||||||
'table' => $this->getPrimaryTableAlias(),
|
'table' => $this->getPrimaryTableAlias(),
|
||||||
|
@ -909,6 +918,8 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$cache->setKey($cache_key, $columns);
|
||||||
|
|
||||||
return $columns;
|
return $columns;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,7 +35,6 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery {
|
||||||
private $workspace = array();
|
private $workspace = array();
|
||||||
private $inFlightPHIDs = array();
|
private $inFlightPHIDs = array();
|
||||||
private $policyFilteredPHIDs = array();
|
private $policyFilteredPHIDs = array();
|
||||||
private $canUseApplication;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Should we continue or throw an exception when a query result is filtered
|
* Should we continue or throw an exception when a query result is filtered
|
||||||
|
@ -679,21 +678,13 @@ abstract class PhabricatorPolicyAwareQuery extends PhabricatorOffsetPagedQuery {
|
||||||
* execute the query.
|
* execute the query.
|
||||||
*/
|
*/
|
||||||
public function canViewerUseQueryApplication() {
|
public function canViewerUseQueryApplication() {
|
||||||
if ($this->canUseApplication === null) {
|
|
||||||
$class = $this->getQueryApplicationClass();
|
$class = $this->getQueryApplicationClass();
|
||||||
if (!$class) {
|
if (!$class) {
|
||||||
$this->canUseApplication = true;
|
return true;
|
||||||
} else {
|
|
||||||
$result = id(new PhabricatorApplicationQuery())
|
|
||||||
->setViewer($this->getViewer())
|
|
||||||
->withClasses(array($class))
|
|
||||||
->execute();
|
|
||||||
|
|
||||||
$this->canUseApplication = (bool)$result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return $this->canUseApplication;
|
$viewer = $this->getViewer();
|
||||||
|
return PhabricatorApplication::isClassInstalledForViewer($class, $viewer);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue