mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-25 00:02:41 +01:00
(stable) Promote 2016 Week 25
This commit is contained in:
commit
59bc6adc0e
124 changed files with 4008 additions and 945 deletions
|
@ -7,7 +7,7 @@
|
||||||
*/
|
*/
|
||||||
return array(
|
return array(
|
||||||
'names' => array(
|
'names' => array(
|
||||||
'core.pkg.css' => '6913fe66',
|
'core.pkg.css' => 'c7fc5aec',
|
||||||
'core.pkg.js' => '10275c16',
|
'core.pkg.js' => '10275c16',
|
||||||
'darkconsole.pkg.js' => 'e7393ebb',
|
'darkconsole.pkg.js' => 'e7393ebb',
|
||||||
'differential.pkg.css' => 'b3eea3f5',
|
'differential.pkg.css' => 'b3eea3f5',
|
||||||
|
@ -81,7 +81,7 @@ return array(
|
||||||
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
|
'rsrc/css/application/owners/owners-path-editor.css' => '2f00933b',
|
||||||
'rsrc/css/application/paste/paste.css' => '1898e534',
|
'rsrc/css/application/paste/paste.css' => '1898e534',
|
||||||
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
'rsrc/css/application/people/people-profile.css' => '2473d929',
|
||||||
'rsrc/css/application/phame/phame.css' => '7448a969',
|
'rsrc/css/application/phame/phame.css' => 'bf6a743f',
|
||||||
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
|
'rsrc/css/application/pholio/pholio-edit.css' => '07676f51',
|
||||||
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
'rsrc/css/application/pholio/pholio-inline-comments.css' => '8e545e49',
|
||||||
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
|
'rsrc/css/application/pholio/pholio.css' => 'ca89d380',
|
||||||
|
@ -128,7 +128,7 @@ return array(
|
||||||
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
'rsrc/css/phui/phui-chart.css' => '6bf6f78e',
|
||||||
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
|
'rsrc/css/phui/phui-crumbs-view.css' => '6b813619',
|
||||||
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
|
'rsrc/css/phui/phui-curtain-view.css' => '7148ae25',
|
||||||
'rsrc/css/phui/phui-document-pro.css' => '8419560b',
|
'rsrc/css/phui/phui-document-pro.css' => 'a3730b94',
|
||||||
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
|
||||||
'rsrc/css/phui/phui-document.css' => '715aedfb',
|
'rsrc/css/phui/phui-document.css' => '715aedfb',
|
||||||
'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
|
'rsrc/css/phui/phui-feed-story.css' => 'aa49845d',
|
||||||
|
@ -155,7 +155,7 @@ return array(
|
||||||
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
'rsrc/css/phui/phui-spacing.css' => '042804d6',
|
||||||
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
'rsrc/css/phui/phui-status.css' => 'd5263e49',
|
||||||
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
'rsrc/css/phui/phui-tag-view.css' => '6bbd83e2',
|
||||||
'rsrc/css/phui/phui-timeline-view.css' => '6e342216',
|
'rsrc/css/phui/phui-timeline-view.css' => '8ea41b25',
|
||||||
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
|
'rsrc/css/phui/phui-two-column-view.css' => '9fb86c85',
|
||||||
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
'rsrc/css/phui/workboards/phui-workboard-color.css' => 'ac6fe6a7',
|
||||||
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
|
'rsrc/css/phui/workboards/phui-workboard.css' => 'e6d89647',
|
||||||
|
@ -808,7 +808,7 @@ return array(
|
||||||
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
'phabricator-uiexample-reactor-sendclass' => '1def2711',
|
||||||
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
'phabricator-uiexample-reactor-sendproperties' => 'b1f0ccee',
|
||||||
'phabricator-zindex-css' => '5b6fcf3f',
|
'phabricator-zindex-css' => '5b6fcf3f',
|
||||||
'phame-css' => '7448a969',
|
'phame-css' => 'bf6a743f',
|
||||||
'pholio-css' => 'ca89d380',
|
'pholio-css' => 'ca89d380',
|
||||||
'pholio-edit-css' => '07676f51',
|
'pholio-edit-css' => '07676f51',
|
||||||
'pholio-inline-comments-css' => '8e545e49',
|
'pholio-inline-comments-css' => '8e545e49',
|
||||||
|
@ -831,7 +831,7 @@ return array(
|
||||||
'phui-curtain-view-css' => '7148ae25',
|
'phui-curtain-view-css' => '7148ae25',
|
||||||
'phui-document-summary-view-css' => '9ca48bdf',
|
'phui-document-summary-view-css' => '9ca48bdf',
|
||||||
'phui-document-view-css' => '715aedfb',
|
'phui-document-view-css' => '715aedfb',
|
||||||
'phui-document-view-pro-css' => '8419560b',
|
'phui-document-view-pro-css' => 'a3730b94',
|
||||||
'phui-feed-story-css' => 'aa49845d',
|
'phui-feed-story-css' => 'aa49845d',
|
||||||
'phui-font-icon-base-css' => '6449bce8',
|
'phui-font-icon-base-css' => '6449bce8',
|
||||||
'phui-fontkit-css' => '9cda225e',
|
'phui-fontkit-css' => '9cda225e',
|
||||||
|
@ -860,7 +860,7 @@ return array(
|
||||||
'phui-status-list-view-css' => 'd5263e49',
|
'phui-status-list-view-css' => 'd5263e49',
|
||||||
'phui-tag-view-css' => '6bbd83e2',
|
'phui-tag-view-css' => '6bbd83e2',
|
||||||
'phui-theme-css' => '027ba77e',
|
'phui-theme-css' => '027ba77e',
|
||||||
'phui-timeline-view-css' => '6e342216',
|
'phui-timeline-view-css' => '8ea41b25',
|
||||||
'phui-two-column-view-css' => '9fb86c85',
|
'phui-two-column-view-css' => '9fb86c85',
|
||||||
'phui-workboard-color-css' => 'ac6fe6a7',
|
'phui-workboard-color-css' => 'ac6fe6a7',
|
||||||
'phui-workboard-view-css' => 'e6d89647',
|
'phui-workboard-view-css' => 'e6d89647',
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||||
|
ADD headerImagePHID VARBINARY(64);
|
6
resources/sql/autopatches/20160616.repo.01.oldref.sql
Normal file
6
resources/sql/autopatches/20160616.repo.01.oldref.sql
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
CREATE TABLE {$NAMESPACE}_repository.repository_oldref (
|
||||||
|
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||||
|
repositoryPHID VARBINARY(64) NOT NULL,
|
||||||
|
commitIdentifier VARCHAR(40) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||||
|
KEY `key_repository` (repositoryPHID)
|
||||||
|
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_harbormaster.harbormaster_buildartifact
|
||||||
|
ADD isReleased BOOL NOT NULL;
|
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_phame.phame_blog
|
||||||
|
ADD subtitle VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -1,47 +1,5 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
$table = new PhabricatorPaste();
|
// Long ago, this migration populated initial "create" transactions for old
|
||||||
$x_table = new PhabricatorPasteTransaction();
|
// pastes from before transactions came into existence. It was removed after
|
||||||
|
// about three years.
|
||||||
$conn_w = $table->establishConnection('w');
|
|
||||||
$conn_w->openTransaction();
|
|
||||||
|
|
||||||
echo pht('Adding transactions for existing paste objects...')."\n";
|
|
||||||
|
|
||||||
$rows = new LiskRawMigrationIterator($conn_w, 'pastebin_paste');
|
|
||||||
foreach ($rows as $row) {
|
|
||||||
|
|
||||||
$id = $row['id'];
|
|
||||||
echo pht('Adding transactions for paste id %d...', $id)."\n";
|
|
||||||
|
|
||||||
$xaction_phid = PhabricatorPHID::generateNewPHID(
|
|
||||||
PhabricatorApplicationTransactionTransactionPHIDType::TYPECONST);
|
|
||||||
|
|
||||||
queryfx(
|
|
||||||
$conn_w,
|
|
||||||
'INSERT INTO %T (phid, authorPHID, objectPHID, viewPolicy, editPolicy,
|
|
||||||
transactionType, oldValue, newValue,
|
|
||||||
contentSource, metadata, dateCreated, dateModified,
|
|
||||||
commentVersion)
|
|
||||||
VALUES (%s, %s, %s, %s, %s, %s, %ns, %ns, %s, %s, %d, %d, %d)',
|
|
||||||
$x_table->getTableName(),
|
|
||||||
$xaction_phid,
|
|
||||||
$row['authorPHID'],
|
|
||||||
$row['phid'],
|
|
||||||
'public',
|
|
||||||
$row['authorPHID'],
|
|
||||||
PhabricatorPasteTransaction::TYPE_CONTENT,
|
|
||||||
'null',
|
|
||||||
$row['filePHID'],
|
|
||||||
PhabricatorContentSource::newForSource(
|
|
||||||
PhabricatorOldWorldContentSource::SOURCECONST)->serialize(),
|
|
||||||
'[]',
|
|
||||||
$row['dateCreated'],
|
|
||||||
$row['dateCreated'],
|
|
||||||
0);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
$conn_w->saveTransaction();
|
|
||||||
|
|
||||||
echo pht('Done.')."\n";
|
|
||||||
|
|
|
@ -4,6 +4,11 @@
|
||||||
// This is a wrapper script for Git, Mercurial, and Subversion. It primarily
|
// This is a wrapper script for Git, Mercurial, and Subversion. It primarily
|
||||||
// serves to inject "-o StrictHostKeyChecking=no" into the SSH arguments.
|
// serves to inject "-o StrictHostKeyChecking=no" into the SSH arguments.
|
||||||
|
|
||||||
|
// In some cases, Subversion sends us SIGTERM. If we don't catch the signal and
|
||||||
|
// react to it, we won't run object destructors by default and thus won't clean
|
||||||
|
// up temporary files. Declare ticks so we can install a signal handler.
|
||||||
|
declare(ticks=1);
|
||||||
|
|
||||||
$root = dirname(dirname(dirname(__FILE__)));
|
$root = dirname(dirname(dirname(__FILE__)));
|
||||||
require_once $root.'/scripts/__init_script__.php';
|
require_once $root.'/scripts/__init_script__.php';
|
||||||
|
|
||||||
|
@ -21,6 +26,16 @@ $args->parsePartial(
|
||||||
));
|
));
|
||||||
$unconsumed_argv = $args->getUnconsumedArgumentVector();
|
$unconsumed_argv = $args->getUnconsumedArgumentVector();
|
||||||
|
|
||||||
|
if (function_exists('pcntl_signal')) {
|
||||||
|
pcntl_signal(SIGTERM, 'ssh_connect_signal');
|
||||||
|
}
|
||||||
|
|
||||||
|
function ssh_connect_signal($signo) {
|
||||||
|
// This is just letting destructors fire. In particular, we want to clean
|
||||||
|
// up any temporary files we wrote. See T10547.
|
||||||
|
exit(128 + $signo);
|
||||||
|
}
|
||||||
|
|
||||||
$pattern = array();
|
$pattern = array();
|
||||||
$arguments = array();
|
$arguments = array();
|
||||||
|
|
||||||
|
|
|
@ -489,6 +489,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php',
|
'DifferentialReleephRequestFieldSpecification' => 'applications/releeph/differential/DifferentialReleephRequestFieldSpecification.php',
|
||||||
'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php',
|
'DifferentialRemarkupRule' => 'applications/differential/remarkup/DifferentialRemarkupRule.php',
|
||||||
'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php',
|
'DifferentialReplyHandler' => 'applications/differential/mail/DifferentialReplyHandler.php',
|
||||||
|
'DifferentialRepositoryDatasource' => 'applications/differential/typeahead/DifferentialRepositoryDatasource.php',
|
||||||
'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php',
|
'DifferentialRepositoryField' => 'applications/differential/customfield/DifferentialRepositoryField.php',
|
||||||
'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php',
|
'DifferentialRepositoryLookup' => 'applications/differential/query/DifferentialRepositoryLookup.php',
|
||||||
'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php',
|
'DifferentialRequiredSignaturesField' => 'applications/differential/customfield/DifferentialRequiredSignaturesField.php',
|
||||||
|
@ -821,6 +822,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionSymbolQuery' => 'applications/diffusion/query/DiffusionSymbolQuery.php',
|
'DiffusionSymbolQuery' => 'applications/diffusion/query/DiffusionSymbolQuery.php',
|
||||||
'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php',
|
'DiffusionTagListController' => 'applications/diffusion/controller/DiffusionTagListController.php',
|
||||||
'DiffusionTagListView' => 'applications/diffusion/view/DiffusionTagListView.php',
|
'DiffusionTagListView' => 'applications/diffusion/view/DiffusionTagListView.php',
|
||||||
|
'DiffusionTaggedRepositoriesFunctionDatasource' => 'applications/diffusion/typeahead/DiffusionTaggedRepositoriesFunctionDatasource.php',
|
||||||
'DiffusionTagsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php',
|
'DiffusionTagsQueryConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionTagsQueryConduitAPIMethod.php',
|
||||||
'DiffusionURIEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionURIEditConduitAPIMethod.php',
|
'DiffusionURIEditConduitAPIMethod' => 'applications/diffusion/conduit/DiffusionURIEditConduitAPIMethod.php',
|
||||||
'DiffusionURIEditEngine' => 'applications/diffusion/editor/DiffusionURIEditEngine.php',
|
'DiffusionURIEditEngine' => 'applications/diffusion/editor/DiffusionURIEditEngine.php',
|
||||||
|
@ -2158,6 +2160,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorController' => 'applications/base/controller/PhabricatorController.php',
|
'PhabricatorController' => 'applications/base/controller/PhabricatorController.php',
|
||||||
'PhabricatorCookies' => 'applications/auth/constants/PhabricatorCookies.php',
|
'PhabricatorCookies' => 'applications/auth/constants/PhabricatorCookies.php',
|
||||||
'PhabricatorCoreConfigOptions' => 'applications/config/option/PhabricatorCoreConfigOptions.php',
|
'PhabricatorCoreConfigOptions' => 'applications/config/option/PhabricatorCoreConfigOptions.php',
|
||||||
|
'PhabricatorCoreCreateTransaction' => 'applications/transactions/xaction/PhabricatorCoreCreateTransaction.php',
|
||||||
|
'PhabricatorCoreTransactionType' => 'applications/transactions/xaction/PhabricatorCoreTransactionType.php',
|
||||||
|
'PhabricatorCoreVoidTransaction' => 'applications/transactions/xaction/PhabricatorCoreVoidTransaction.php',
|
||||||
'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php',
|
'PhabricatorCountdown' => 'applications/countdown/storage/PhabricatorCountdown.php',
|
||||||
'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php',
|
'PhabricatorCountdownApplication' => 'applications/countdown/application/PhabricatorCountdownApplication.php',
|
||||||
'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php',
|
'PhabricatorCountdownController' => 'applications/countdown/controller/PhabricatorCountdownController.php',
|
||||||
|
@ -2463,6 +2468,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php',
|
'PhabricatorFeedStoryPublisher' => 'applications/feed/PhabricatorFeedStoryPublisher.php',
|
||||||
'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php',
|
'PhabricatorFeedStoryReference' => 'applications/feed/storage/PhabricatorFeedStoryReference.php',
|
||||||
'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php',
|
'PhabricatorFile' => 'applications/files/storage/PhabricatorFile.php',
|
||||||
|
'PhabricatorFileAES256StorageFormat' => 'applications/files/format/PhabricatorFileAES256StorageFormat.php',
|
||||||
'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php',
|
'PhabricatorFileBundleLoader' => 'applications/files/query/PhabricatorFileBundleLoader.php',
|
||||||
'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php',
|
'PhabricatorFileChunk' => 'applications/files/storage/PhabricatorFileChunk.php',
|
||||||
'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php',
|
'PhabricatorFileChunkIterator' => 'applications/files/engine/PhabricatorFileChunkIterator.php',
|
||||||
|
@ -2485,12 +2491,16 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
||||||
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
||||||
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
||||||
|
'PhabricatorFileROT13StorageFormat' => 'applications/files/format/PhabricatorFileROT13StorageFormat.php',
|
||||||
|
'PhabricatorFileRawStorageFormat' => 'applications/files/format/PhabricatorFileRawStorageFormat.php',
|
||||||
'PhabricatorFileSchemaSpec' => 'applications/files/storage/PhabricatorFileSchemaSpec.php',
|
'PhabricatorFileSchemaSpec' => 'applications/files/storage/PhabricatorFileSchemaSpec.php',
|
||||||
'PhabricatorFileSearchEngine' => 'applications/files/query/PhabricatorFileSearchEngine.php',
|
'PhabricatorFileSearchEngine' => 'applications/files/query/PhabricatorFileSearchEngine.php',
|
||||||
'PhabricatorFileStorageBlob' => 'applications/files/storage/PhabricatorFileStorageBlob.php',
|
'PhabricatorFileStorageBlob' => 'applications/files/storage/PhabricatorFileStorageBlob.php',
|
||||||
'PhabricatorFileStorageConfigurationException' => 'applications/files/exception/PhabricatorFileStorageConfigurationException.php',
|
'PhabricatorFileStorageConfigurationException' => 'applications/files/exception/PhabricatorFileStorageConfigurationException.php',
|
||||||
'PhabricatorFileStorageEngine' => 'applications/files/engine/PhabricatorFileStorageEngine.php',
|
'PhabricatorFileStorageEngine' => 'applications/files/engine/PhabricatorFileStorageEngine.php',
|
||||||
'PhabricatorFileStorageEngineTestCase' => 'applications/files/engine/__tests__/PhabricatorFileStorageEngineTestCase.php',
|
'PhabricatorFileStorageEngineTestCase' => 'applications/files/engine/__tests__/PhabricatorFileStorageEngineTestCase.php',
|
||||||
|
'PhabricatorFileStorageFormat' => 'applications/files/format/PhabricatorFileStorageFormat.php',
|
||||||
|
'PhabricatorFileStorageFormatTestCase' => 'applications/files/format/__tests__/PhabricatorFileStorageFormatTestCase.php',
|
||||||
'PhabricatorFileTemporaryGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php',
|
'PhabricatorFileTemporaryGarbageCollector' => 'applications/files/garbagecollector/PhabricatorFileTemporaryGarbageCollector.php',
|
||||||
'PhabricatorFileTestCase' => 'applications/files/storage/__tests__/PhabricatorFileTestCase.php',
|
'PhabricatorFileTestCase' => 'applications/files/storage/__tests__/PhabricatorFileTestCase.php',
|
||||||
'PhabricatorFileTestDataGenerator' => 'applications/files/lipsum/PhabricatorFileTestDataGenerator.php',
|
'PhabricatorFileTestDataGenerator' => 'applications/files/lipsum/PhabricatorFileTestDataGenerator.php',
|
||||||
|
@ -2514,7 +2524,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFilesConfigOptions' => 'applications/files/config/PhabricatorFilesConfigOptions.php',
|
'PhabricatorFilesConfigOptions' => 'applications/files/config/PhabricatorFilesConfigOptions.php',
|
||||||
'PhabricatorFilesManagementCatWorkflow' => 'applications/files/management/PhabricatorFilesManagementCatWorkflow.php',
|
'PhabricatorFilesManagementCatWorkflow' => 'applications/files/management/PhabricatorFilesManagementCatWorkflow.php',
|
||||||
'PhabricatorFilesManagementCompactWorkflow' => 'applications/files/management/PhabricatorFilesManagementCompactWorkflow.php',
|
'PhabricatorFilesManagementCompactWorkflow' => 'applications/files/management/PhabricatorFilesManagementCompactWorkflow.php',
|
||||||
|
'PhabricatorFilesManagementCycleWorkflow' => 'applications/files/management/PhabricatorFilesManagementCycleWorkflow.php',
|
||||||
|
'PhabricatorFilesManagementEncodeWorkflow' => 'applications/files/management/PhabricatorFilesManagementEncodeWorkflow.php',
|
||||||
'PhabricatorFilesManagementEnginesWorkflow' => 'applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php',
|
'PhabricatorFilesManagementEnginesWorkflow' => 'applications/files/management/PhabricatorFilesManagementEnginesWorkflow.php',
|
||||||
|
'PhabricatorFilesManagementGenerateKeyWorkflow' => 'applications/files/management/PhabricatorFilesManagementGenerateKeyWorkflow.php',
|
||||||
'PhabricatorFilesManagementMigrateWorkflow' => 'applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php',
|
'PhabricatorFilesManagementMigrateWorkflow' => 'applications/files/management/PhabricatorFilesManagementMigrateWorkflow.php',
|
||||||
'PhabricatorFilesManagementPurgeWorkflow' => 'applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php',
|
'PhabricatorFilesManagementPurgeWorkflow' => 'applications/files/management/PhabricatorFilesManagementPurgeWorkflow.php',
|
||||||
'PhabricatorFilesManagementRebuildWorkflow' => 'applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php',
|
'PhabricatorFilesManagementRebuildWorkflow' => 'applications/files/management/PhabricatorFilesManagementRebuildWorkflow.php',
|
||||||
|
@ -2617,6 +2630,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
|
'PhabricatorJiraIssueHasObjectEdgeType' => 'applications/doorkeeper/edge/PhabricatorJiraIssueHasObjectEdgeType.php',
|
||||||
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
'PhabricatorJumpNavHandler' => 'applications/search/engine/PhabricatorJumpNavHandler.php',
|
||||||
'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php',
|
'PhabricatorKeyValueDatabaseCache' => 'applications/cache/PhabricatorKeyValueDatabaseCache.php',
|
||||||
|
'PhabricatorKeyring' => 'applications/files/keyring/PhabricatorKeyring.php',
|
||||||
|
'PhabricatorKeyringConfigOptionType' => 'applications/files/keyring/PhabricatorKeyringConfigOptionType.php',
|
||||||
'PhabricatorLDAPAuthProvider' => 'applications/auth/provider/PhabricatorLDAPAuthProvider.php',
|
'PhabricatorLDAPAuthProvider' => 'applications/auth/provider/PhabricatorLDAPAuthProvider.php',
|
||||||
'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php',
|
'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php',
|
||||||
'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php',
|
'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php',
|
||||||
|
@ -2755,6 +2770,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php',
|
'PhabricatorMetaMTASendGridReceiveController' => 'applications/metamta/controller/PhabricatorMetaMTASendGridReceiveController.php',
|
||||||
'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php',
|
'PhabricatorMetaMTAWorker' => 'applications/metamta/PhabricatorMetaMTAWorker.php',
|
||||||
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
|
'PhabricatorMetronomicTriggerClock' => 'infrastructure/daemon/workers/clock/PhabricatorMetronomicTriggerClock.php',
|
||||||
|
'PhabricatorModularTransaction' => 'applications/transactions/storage/PhabricatorModularTransaction.php',
|
||||||
|
'PhabricatorModularTransactionType' => 'applications/transactions/storage/PhabricatorModularTransactionType.php',
|
||||||
'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php',
|
'PhabricatorMonospacedFontSetting' => 'applications/settings/setting/PhabricatorMonospacedFontSetting.php',
|
||||||
'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php',
|
'PhabricatorMonospacedTextareasSetting' => 'applications/settings/setting/PhabricatorMonospacedTextareasSetting.php',
|
||||||
'PhabricatorMotivatorProfilePanel' => 'applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php',
|
'PhabricatorMotivatorProfilePanel' => 'applications/search/profilepanel/PhabricatorMotivatorProfilePanel.php',
|
||||||
|
@ -2914,12 +2931,14 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPasteArchiveController' => 'applications/paste/controller/PhabricatorPasteArchiveController.php',
|
'PhabricatorPasteArchiveController' => 'applications/paste/controller/PhabricatorPasteArchiveController.php',
|
||||||
'PhabricatorPasteConfigOptions' => 'applications/paste/config/PhabricatorPasteConfigOptions.php',
|
'PhabricatorPasteConfigOptions' => 'applications/paste/config/PhabricatorPasteConfigOptions.php',
|
||||||
'PhabricatorPasteContentSearchEngineAttachment' => 'applications/paste/engineextension/PhabricatorPasteContentSearchEngineAttachment.php',
|
'PhabricatorPasteContentSearchEngineAttachment' => 'applications/paste/engineextension/PhabricatorPasteContentSearchEngineAttachment.php',
|
||||||
|
'PhabricatorPasteContentTransaction' => 'applications/paste/xaction/PhabricatorPasteContentTransaction.php',
|
||||||
'PhabricatorPasteController' => 'applications/paste/controller/PhabricatorPasteController.php',
|
'PhabricatorPasteController' => 'applications/paste/controller/PhabricatorPasteController.php',
|
||||||
'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php',
|
'PhabricatorPasteDAO' => 'applications/paste/storage/PhabricatorPasteDAO.php',
|
||||||
'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php',
|
'PhabricatorPasteEditController' => 'applications/paste/controller/PhabricatorPasteEditController.php',
|
||||||
'PhabricatorPasteEditEngine' => 'applications/paste/editor/PhabricatorPasteEditEngine.php',
|
'PhabricatorPasteEditEngine' => 'applications/paste/editor/PhabricatorPasteEditEngine.php',
|
||||||
'PhabricatorPasteEditor' => 'applications/paste/editor/PhabricatorPasteEditor.php',
|
'PhabricatorPasteEditor' => 'applications/paste/editor/PhabricatorPasteEditor.php',
|
||||||
'PhabricatorPasteFilenameContextFreeGrammar' => 'applications/paste/lipsum/PhabricatorPasteFilenameContextFreeGrammar.php',
|
'PhabricatorPasteFilenameContextFreeGrammar' => 'applications/paste/lipsum/PhabricatorPasteFilenameContextFreeGrammar.php',
|
||||||
|
'PhabricatorPasteLanguageTransaction' => 'applications/paste/xaction/PhabricatorPasteLanguageTransaction.php',
|
||||||
'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php',
|
'PhabricatorPasteListController' => 'applications/paste/controller/PhabricatorPasteListController.php',
|
||||||
'PhabricatorPastePastePHIDType' => 'applications/paste/phid/PhabricatorPastePastePHIDType.php',
|
'PhabricatorPastePastePHIDType' => 'applications/paste/phid/PhabricatorPastePastePHIDType.php',
|
||||||
'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php',
|
'PhabricatorPasteQuery' => 'applications/paste/query/PhabricatorPasteQuery.php',
|
||||||
|
@ -2928,10 +2947,13 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPasteSchemaSpec' => 'applications/paste/storage/PhabricatorPasteSchemaSpec.php',
|
'PhabricatorPasteSchemaSpec' => 'applications/paste/storage/PhabricatorPasteSchemaSpec.php',
|
||||||
'PhabricatorPasteSearchEngine' => 'applications/paste/query/PhabricatorPasteSearchEngine.php',
|
'PhabricatorPasteSearchEngine' => 'applications/paste/query/PhabricatorPasteSearchEngine.php',
|
||||||
'PhabricatorPasteSnippet' => 'applications/paste/snippet/PhabricatorPasteSnippet.php',
|
'PhabricatorPasteSnippet' => 'applications/paste/snippet/PhabricatorPasteSnippet.php',
|
||||||
|
'PhabricatorPasteStatusTransaction' => 'applications/paste/xaction/PhabricatorPasteStatusTransaction.php',
|
||||||
'PhabricatorPasteTestDataGenerator' => 'applications/paste/lipsum/PhabricatorPasteTestDataGenerator.php',
|
'PhabricatorPasteTestDataGenerator' => 'applications/paste/lipsum/PhabricatorPasteTestDataGenerator.php',
|
||||||
|
'PhabricatorPasteTitleTransaction' => 'applications/paste/xaction/PhabricatorPasteTitleTransaction.php',
|
||||||
'PhabricatorPasteTransaction' => 'applications/paste/storage/PhabricatorPasteTransaction.php',
|
'PhabricatorPasteTransaction' => 'applications/paste/storage/PhabricatorPasteTransaction.php',
|
||||||
'PhabricatorPasteTransactionComment' => 'applications/paste/storage/PhabricatorPasteTransactionComment.php',
|
'PhabricatorPasteTransactionComment' => 'applications/paste/storage/PhabricatorPasteTransactionComment.php',
|
||||||
'PhabricatorPasteTransactionQuery' => 'applications/paste/query/PhabricatorPasteTransactionQuery.php',
|
'PhabricatorPasteTransactionQuery' => 'applications/paste/query/PhabricatorPasteTransactionQuery.php',
|
||||||
|
'PhabricatorPasteTransactionType' => 'applications/paste/xaction/PhabricatorPasteTransactionType.php',
|
||||||
'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php',
|
'PhabricatorPasteViewController' => 'applications/paste/controller/PhabricatorPasteViewController.php',
|
||||||
'PhabricatorPathSetupCheck' => 'applications/config/check/PhabricatorPathSetupCheck.php',
|
'PhabricatorPathSetupCheck' => 'applications/config/check/PhabricatorPathSetupCheck.php',
|
||||||
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
|
'PhabricatorPeopleAnyOwnerDatasource' => 'applications/people/typeahead/PhabricatorPeopleAnyOwnerDatasource.php',
|
||||||
|
@ -3241,6 +3263,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryManagementListWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php',
|
'PhabricatorRepositoryManagementListWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementListWorkflow.php',
|
||||||
'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php',
|
'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementLookupUsersWorkflow.php',
|
||||||
'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php',
|
'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkImportedWorkflow.php',
|
||||||
|
'PhabricatorRepositoryManagementMarkReachableWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMarkReachableWorkflow.php',
|
||||||
'PhabricatorRepositoryManagementMirrorWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php',
|
'PhabricatorRepositoryManagementMirrorWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMirrorWorkflow.php',
|
||||||
'PhabricatorRepositoryManagementMovePathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php',
|
'PhabricatorRepositoryManagementMovePathsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementMovePathsWorkflow.php',
|
||||||
'PhabricatorRepositoryManagementParentsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php',
|
'PhabricatorRepositoryManagementParentsWorkflow' => 'applications/repository/management/PhabricatorRepositoryManagementParentsWorkflow.php',
|
||||||
|
@ -3254,6 +3277,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php',
|
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'applications/repository/worker/commitmessageparser/PhabricatorRepositoryMercurialCommitMessageParserWorker.php',
|
||||||
'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php',
|
'PhabricatorRepositoryMirror' => 'applications/repository/storage/PhabricatorRepositoryMirror.php',
|
||||||
'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php',
|
'PhabricatorRepositoryMirrorEngine' => 'applications/repository/engine/PhabricatorRepositoryMirrorEngine.php',
|
||||||
|
'PhabricatorRepositoryOldRef' => 'applications/repository/storage/PhabricatorRepositoryOldRef.php',
|
||||||
'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php',
|
'PhabricatorRepositoryParsedChange' => 'applications/repository/data/PhabricatorRepositoryParsedChange.php',
|
||||||
'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php',
|
'PhabricatorRepositoryPullEngine' => 'applications/repository/engine/PhabricatorRepositoryPullEngine.php',
|
||||||
'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php',
|
'PhabricatorRepositoryPullEvent' => 'applications/repository/storage/PhabricatorRepositoryPullEvent.php',
|
||||||
|
@ -3589,6 +3613,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorTokensCurtainExtension' => 'applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php',
|
'PhabricatorTokensCurtainExtension' => 'applications/tokens/engineextension/PhabricatorTokensCurtainExtension.php',
|
||||||
'PhabricatorTokensSettingsPanel' => 'applications/settings/panel/PhabricatorTokensSettingsPanel.php',
|
'PhabricatorTokensSettingsPanel' => 'applications/settings/panel/PhabricatorTokensSettingsPanel.php',
|
||||||
'PhabricatorTooltipUIExample' => 'applications/uiexample/examples/PhabricatorTooltipUIExample.php',
|
'PhabricatorTooltipUIExample' => 'applications/uiexample/examples/PhabricatorTooltipUIExample.php',
|
||||||
|
'PhabricatorTransactionChange' => 'applications/transactions/data/PhabricatorTransactionChange.php',
|
||||||
|
'PhabricatorTransactionRemarkupChange' => 'applications/transactions/data/PhabricatorTransactionRemarkupChange.php',
|
||||||
'PhabricatorTransactions' => 'applications/transactions/constants/PhabricatorTransactions.php',
|
'PhabricatorTransactions' => 'applications/transactions/constants/PhabricatorTransactions.php',
|
||||||
'PhabricatorTransactionsApplication' => 'applications/transactions/application/PhabricatorTransactionsApplication.php',
|
'PhabricatorTransactionsApplication' => 'applications/transactions/application/PhabricatorTransactionsApplication.php',
|
||||||
'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php',
|
'PhabricatorTransactionsDestructionEngineExtension' => 'applications/transactions/engineextension/PhabricatorTransactionsDestructionEngineExtension.php',
|
||||||
|
@ -3750,6 +3776,8 @@ phutil_register_library_map(array(
|
||||||
'PhameBlogEditEngine' => 'applications/phame/editor/PhameBlogEditEngine.php',
|
'PhameBlogEditEngine' => 'applications/phame/editor/PhameBlogEditEngine.php',
|
||||||
'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php',
|
'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php',
|
||||||
'PhameBlogFeedController' => 'applications/phame/controller/blog/PhameBlogFeedController.php',
|
'PhameBlogFeedController' => 'applications/phame/controller/blog/PhameBlogFeedController.php',
|
||||||
|
'PhameBlogFulltextEngine' => 'applications/phame/search/PhameBlogFulltextEngine.php',
|
||||||
|
'PhameBlogHeaderPictureController' => 'applications/phame/controller/blog/PhameBlogHeaderPictureController.php',
|
||||||
'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
|
'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php',
|
||||||
'PhameBlogListView' => 'applications/phame/view/PhameBlogListView.php',
|
'PhameBlogListView' => 'applications/phame/view/PhameBlogListView.php',
|
||||||
'PhameBlogManageController' => 'applications/phame/controller/blog/PhameBlogManageController.php',
|
'PhameBlogManageController' => 'applications/phame/controller/blog/PhameBlogManageController.php',
|
||||||
|
@ -3771,12 +3799,14 @@ phutil_register_library_map(array(
|
||||||
'PhameLiveController' => 'applications/phame/controller/PhameLiveController.php',
|
'PhameLiveController' => 'applications/phame/controller/PhameLiveController.php',
|
||||||
'PhameNextPostView' => 'applications/phame/view/PhameNextPostView.php',
|
'PhameNextPostView' => 'applications/phame/view/PhameNextPostView.php',
|
||||||
'PhamePost' => 'applications/phame/storage/PhamePost.php',
|
'PhamePost' => 'applications/phame/storage/PhamePost.php',
|
||||||
|
'PhamePostArchiveController' => 'applications/phame/controller/post/PhamePostArchiveController.php',
|
||||||
'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php',
|
'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php',
|
||||||
'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php',
|
'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php',
|
||||||
'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php',
|
'PhamePostEditConduitAPIMethod' => 'applications/phame/conduit/PhamePostEditConduitAPIMethod.php',
|
||||||
'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php',
|
'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php',
|
||||||
'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
|
'PhamePostEditEngine' => 'applications/phame/editor/PhamePostEditEngine.php',
|
||||||
'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
|
'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php',
|
||||||
|
'PhamePostFulltextEngine' => 'applications/phame/search/PhamePostFulltextEngine.php',
|
||||||
'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php',
|
'PhamePostHistoryController' => 'applications/phame/controller/post/PhamePostHistoryController.php',
|
||||||
'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
|
'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php',
|
||||||
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
|
'PhamePostListView' => 'applications/phame/view/PhamePostListView.php',
|
||||||
|
@ -4787,6 +4817,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialReleephRequestFieldSpecification' => 'Phobject',
|
'DifferentialReleephRequestFieldSpecification' => 'Phobject',
|
||||||
'DifferentialRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
'DifferentialRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||||
'DifferentialReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
'DifferentialReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||||
|
'DifferentialRepositoryDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'DifferentialRepositoryField' => 'DifferentialCoreCustomField',
|
'DifferentialRepositoryField' => 'DifferentialCoreCustomField',
|
||||||
'DifferentialRepositoryLookup' => 'Phobject',
|
'DifferentialRepositoryLookup' => 'Phobject',
|
||||||
'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField',
|
'DifferentialRequiredSignaturesField' => 'DifferentialCoreCustomField',
|
||||||
|
@ -5137,6 +5168,7 @@ phutil_register_library_map(array(
|
||||||
'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery',
|
'DiffusionSymbolQuery' => 'PhabricatorOffsetPagedQuery',
|
||||||
'DiffusionTagListController' => 'DiffusionController',
|
'DiffusionTagListController' => 'DiffusionController',
|
||||||
'DiffusionTagListView' => 'DiffusionView',
|
'DiffusionTagListView' => 'DiffusionView',
|
||||||
|
'DiffusionTaggedRepositoriesFunctionDatasource' => 'PhabricatorTypeaheadCompositeDatasource',
|
||||||
'DiffusionTagsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
'DiffusionTagsQueryConduitAPIMethod' => 'DiffusionQueryConduitAPIMethod',
|
||||||
'DiffusionURIEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
'DiffusionURIEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||||
'DiffusionURIEditEngine' => 'PhabricatorEditEngine',
|
'DiffusionURIEditEngine' => 'PhabricatorEditEngine',
|
||||||
|
@ -6722,6 +6754,9 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorController' => 'AphrontController',
|
'PhabricatorController' => 'AphrontController',
|
||||||
'PhabricatorCookies' => 'Phobject',
|
'PhabricatorCookies' => 'Phobject',
|
||||||
'PhabricatorCoreConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorCoreConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
'PhabricatorCoreCreateTransaction' => 'PhabricatorCoreTransactionType',
|
||||||
|
'PhabricatorCoreTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
|
'PhabricatorCoreVoidTransaction' => 'PhabricatorModularTransactionType',
|
||||||
'PhabricatorCountdown' => array(
|
'PhabricatorCountdown' => array(
|
||||||
'PhabricatorCountdownDAO',
|
'PhabricatorCountdownDAO',
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
|
@ -7076,6 +7111,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPolicyInterface',
|
'PhabricatorPolicyInterface',
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
),
|
),
|
||||||
|
'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat',
|
||||||
'PhabricatorFileBundleLoader' => 'Phobject',
|
'PhabricatorFileBundleLoader' => 'Phobject',
|
||||||
'PhabricatorFileChunk' => array(
|
'PhabricatorFileChunk' => array(
|
||||||
'PhabricatorFileDAO',
|
'PhabricatorFileDAO',
|
||||||
|
@ -7112,12 +7148,16 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFileLinkView' => 'AphrontView',
|
'PhabricatorFileLinkView' => 'AphrontView',
|
||||||
'PhabricatorFileListController' => 'PhabricatorFileController',
|
'PhabricatorFileListController' => 'PhabricatorFileController',
|
||||||
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
'PhabricatorFileROT13StorageFormat' => 'PhabricatorFileStorageFormat',
|
||||||
|
'PhabricatorFileRawStorageFormat' => 'PhabricatorFileStorageFormat',
|
||||||
'PhabricatorFileSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'PhabricatorFileSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'PhabricatorFileSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhabricatorFileSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
|
'PhabricatorFileStorageBlob' => 'PhabricatorFileDAO',
|
||||||
'PhabricatorFileStorageConfigurationException' => 'Exception',
|
'PhabricatorFileStorageConfigurationException' => 'Exception',
|
||||||
'PhabricatorFileStorageEngine' => 'Phobject',
|
'PhabricatorFileStorageEngine' => 'Phobject',
|
||||||
'PhabricatorFileStorageEngineTestCase' => 'PhabricatorTestCase',
|
'PhabricatorFileStorageEngineTestCase' => 'PhabricatorTestCase',
|
||||||
|
'PhabricatorFileStorageFormat' => 'Phobject',
|
||||||
|
'PhabricatorFileStorageFormatTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorFileTemporaryGarbageCollector' => 'PhabricatorGarbageCollector',
|
'PhabricatorFileTemporaryGarbageCollector' => 'PhabricatorGarbageCollector',
|
||||||
'PhabricatorFileTestCase' => 'PhabricatorTestCase',
|
'PhabricatorFileTestCase' => 'PhabricatorTestCase',
|
||||||
'PhabricatorFileTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
'PhabricatorFileTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||||
|
@ -7141,7 +7181,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorFilesConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorFilesConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorFilesManagementCatWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
'PhabricatorFilesManagementCatWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
'PhabricatorFilesManagementCompactWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
'PhabricatorFilesManagementCompactWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
|
'PhabricatorFilesManagementCycleWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
|
'PhabricatorFilesManagementEncodeWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
'PhabricatorFilesManagementEnginesWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
'PhabricatorFilesManagementEnginesWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
|
'PhabricatorFilesManagementGenerateKeyWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
'PhabricatorFilesManagementMigrateWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
'PhabricatorFilesManagementMigrateWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
'PhabricatorFilesManagementPurgeWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
'PhabricatorFilesManagementPurgeWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
'PhabricatorFilesManagementRebuildWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
'PhabricatorFilesManagementRebuildWorkflow' => 'PhabricatorFilesManagementWorkflow',
|
||||||
|
@ -7251,6 +7294,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType',
|
'PhabricatorJiraIssueHasObjectEdgeType' => 'PhabricatorEdgeType',
|
||||||
'PhabricatorJumpNavHandler' => 'Phobject',
|
'PhabricatorJumpNavHandler' => 'Phobject',
|
||||||
'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache',
|
'PhabricatorKeyValueDatabaseCache' => 'PhutilKeyValueCache',
|
||||||
|
'PhabricatorKeyring' => 'Phobject',
|
||||||
|
'PhabricatorKeyringConfigOptionType' => 'PhabricatorConfigJSONOptionType',
|
||||||
'PhabricatorLDAPAuthProvider' => 'PhabricatorAuthProvider',
|
'PhabricatorLDAPAuthProvider' => 'PhabricatorAuthProvider',
|
||||||
'PhabricatorLegalpadApplication' => 'PhabricatorApplication',
|
'PhabricatorLegalpadApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorLegalpadConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
|
@ -7399,6 +7444,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
|
'PhabricatorMetaMTASendGridReceiveController' => 'PhabricatorMetaMTAController',
|
||||||
'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
|
'PhabricatorMetaMTAWorker' => 'PhabricatorWorker',
|
||||||
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
|
'PhabricatorMetronomicTriggerClock' => 'PhabricatorTriggerClock',
|
||||||
|
'PhabricatorModularTransaction' => 'PhabricatorApplicationTransaction',
|
||||||
|
'PhabricatorModularTransactionType' => 'Phobject',
|
||||||
'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting',
|
'PhabricatorMonospacedFontSetting' => 'PhabricatorStringSetting',
|
||||||
'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting',
|
'PhabricatorMonospacedTextareasSetting' => 'PhabricatorSelectSetting',
|
||||||
'PhabricatorMotivatorProfilePanel' => 'PhabricatorProfilePanel',
|
'PhabricatorMotivatorProfilePanel' => 'PhabricatorProfilePanel',
|
||||||
|
@ -7594,12 +7641,14 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPasteArchiveController' => 'PhabricatorPasteController',
|
'PhabricatorPasteArchiveController' => 'PhabricatorPasteController',
|
||||||
'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
'PhabricatorPasteConfigOptions' => 'PhabricatorApplicationConfigOptions',
|
||||||
'PhabricatorPasteContentSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
'PhabricatorPasteContentSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||||
|
'PhabricatorPasteContentTransaction' => 'PhabricatorPasteTransactionType',
|
||||||
'PhabricatorPasteController' => 'PhabricatorController',
|
'PhabricatorPasteController' => 'PhabricatorController',
|
||||||
'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
|
'PhabricatorPasteDAO' => 'PhabricatorLiskDAO',
|
||||||
'PhabricatorPasteEditController' => 'PhabricatorPasteController',
|
'PhabricatorPasteEditController' => 'PhabricatorPasteController',
|
||||||
'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine',
|
'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine',
|
||||||
'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor',
|
'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
'PhabricatorPasteFilenameContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
'PhabricatorPasteFilenameContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
||||||
|
'PhabricatorPasteLanguageTransaction' => 'PhabricatorPasteTransactionType',
|
||||||
'PhabricatorPasteListController' => 'PhabricatorPasteController',
|
'PhabricatorPasteListController' => 'PhabricatorPasteController',
|
||||||
'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType',
|
'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType',
|
||||||
'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
'PhabricatorPasteQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||||
|
@ -7608,10 +7657,13 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorPasteSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'PhabricatorPasteSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'PhabricatorPasteSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
'PhabricatorPasteSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||||
'PhabricatorPasteSnippet' => 'Phobject',
|
'PhabricatorPasteSnippet' => 'Phobject',
|
||||||
|
'PhabricatorPasteStatusTransaction' => 'PhabricatorPasteTransactionType',
|
||||||
'PhabricatorPasteTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
'PhabricatorPasteTestDataGenerator' => 'PhabricatorTestDataGenerator',
|
||||||
'PhabricatorPasteTransaction' => 'PhabricatorApplicationTransaction',
|
'PhabricatorPasteTitleTransaction' => 'PhabricatorPasteTransactionType',
|
||||||
|
'PhabricatorPasteTransaction' => 'PhabricatorModularTransaction',
|
||||||
'PhabricatorPasteTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
'PhabricatorPasteTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||||
'PhabricatorPasteTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
'PhabricatorPasteTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||||
|
'PhabricatorPasteTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'PhabricatorPasteViewController' => 'PhabricatorPasteController',
|
'PhabricatorPasteViewController' => 'PhabricatorPasteController',
|
||||||
'PhabricatorPathSetupCheck' => 'PhabricatorSetupCheck',
|
'PhabricatorPathSetupCheck' => 'PhabricatorSetupCheck',
|
||||||
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
'PhabricatorPeopleAnyOwnerDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||||
|
@ -8003,6 +8055,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryManagementListWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
'PhabricatorRepositoryManagementListWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
'PhabricatorRepositoryManagementLookupUsersWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
'PhabricatorRepositoryManagementMarkImportedWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
|
'PhabricatorRepositoryManagementMarkReachableWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
'PhabricatorRepositoryManagementMirrorWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
'PhabricatorRepositoryManagementMirrorWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
'PhabricatorRepositoryManagementMovePathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
'PhabricatorRepositoryManagementMovePathsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
'PhabricatorRepositoryManagementParentsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
'PhabricatorRepositoryManagementParentsWorkflow' => 'PhabricatorRepositoryManagementWorkflow',
|
||||||
|
@ -8016,6 +8069,10 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
'PhabricatorRepositoryMercurialCommitMessageParserWorker' => 'PhabricatorRepositoryCommitMessageParserWorker',
|
||||||
'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO',
|
'PhabricatorRepositoryMirror' => 'PhabricatorRepositoryDAO',
|
||||||
'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine',
|
'PhabricatorRepositoryMirrorEngine' => 'PhabricatorRepositoryEngine',
|
||||||
|
'PhabricatorRepositoryOldRef' => array(
|
||||||
|
'PhabricatorRepositoryDAO',
|
||||||
|
'PhabricatorPolicyInterface',
|
||||||
|
),
|
||||||
'PhabricatorRepositoryParsedChange' => 'Phobject',
|
'PhabricatorRepositoryParsedChange' => 'Phobject',
|
||||||
'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine',
|
'PhabricatorRepositoryPullEngine' => 'PhabricatorRepositoryEngine',
|
||||||
'PhabricatorRepositoryPullEvent' => array(
|
'PhabricatorRepositoryPullEvent' => array(
|
||||||
|
@ -8392,6 +8449,8 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorTokensCurtainExtension' => 'PHUICurtainExtension',
|
'PhabricatorTokensCurtainExtension' => 'PHUICurtainExtension',
|
||||||
'PhabricatorTokensSettingsPanel' => 'PhabricatorSettingsPanel',
|
'PhabricatorTokensSettingsPanel' => 'PhabricatorSettingsPanel',
|
||||||
'PhabricatorTooltipUIExample' => 'PhabricatorUIExample',
|
'PhabricatorTooltipUIExample' => 'PhabricatorUIExample',
|
||||||
|
'PhabricatorTransactionChange' => 'Phobject',
|
||||||
|
'PhabricatorTransactionRemarkupChange' => 'PhabricatorTransactionChange',
|
||||||
'PhabricatorTransactions' => 'Phobject',
|
'PhabricatorTransactions' => 'Phobject',
|
||||||
'PhabricatorTransactionsApplication' => 'PhabricatorApplication',
|
'PhabricatorTransactionsApplication' => 'PhabricatorApplication',
|
||||||
'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
'PhabricatorTransactionsDestructionEngineExtension' => 'PhabricatorDestructionEngineExtension',
|
||||||
|
@ -8585,6 +8644,7 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
'PhabricatorApplicationTransactionInterface',
|
'PhabricatorApplicationTransactionInterface',
|
||||||
'PhabricatorConduitResultInterface',
|
'PhabricatorConduitResultInterface',
|
||||||
|
'PhabricatorFulltextInterface',
|
||||||
),
|
),
|
||||||
'PhameBlog404Controller' => 'PhameLiveController',
|
'PhameBlog404Controller' => 'PhameLiveController',
|
||||||
'PhameBlogArchiveController' => 'PhameBlogController',
|
'PhameBlogArchiveController' => 'PhameBlogController',
|
||||||
|
@ -8595,6 +8655,8 @@ phutil_register_library_map(array(
|
||||||
'PhameBlogEditEngine' => 'PhabricatorEditEngine',
|
'PhameBlogEditEngine' => 'PhabricatorEditEngine',
|
||||||
'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor',
|
'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
'PhameBlogFeedController' => 'PhameBlogController',
|
'PhameBlogFeedController' => 'PhameBlogController',
|
||||||
|
'PhameBlogFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||||
|
'PhameBlogHeaderPictureController' => 'PhameBlogController',
|
||||||
'PhameBlogListController' => 'PhameBlogController',
|
'PhameBlogListController' => 'PhameBlogController',
|
||||||
'PhameBlogListView' => 'AphrontTagView',
|
'PhameBlogListView' => 'AphrontTagView',
|
||||||
'PhameBlogManageController' => 'PhameBlogController',
|
'PhameBlogManageController' => 'PhameBlogController',
|
||||||
|
@ -8626,13 +8688,16 @@ phutil_register_library_map(array(
|
||||||
'PhabricatorDestructibleInterface',
|
'PhabricatorDestructibleInterface',
|
||||||
'PhabricatorTokenReceiverInterface',
|
'PhabricatorTokenReceiverInterface',
|
||||||
'PhabricatorConduitResultInterface',
|
'PhabricatorConduitResultInterface',
|
||||||
|
'PhabricatorFulltextInterface',
|
||||||
),
|
),
|
||||||
|
'PhamePostArchiveController' => 'PhamePostController',
|
||||||
'PhamePostCommentController' => 'PhamePostController',
|
'PhamePostCommentController' => 'PhamePostController',
|
||||||
'PhamePostController' => 'PhameController',
|
'PhamePostController' => 'PhameController',
|
||||||
'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
'PhamePostEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
|
||||||
'PhamePostEditController' => 'PhamePostController',
|
'PhamePostEditController' => 'PhamePostController',
|
||||||
'PhamePostEditEngine' => 'PhabricatorEditEngine',
|
'PhamePostEditEngine' => 'PhabricatorEditEngine',
|
||||||
'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
|
'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||||
|
'PhamePostFulltextEngine' => 'PhabricatorFulltextEngine',
|
||||||
'PhamePostHistoryController' => 'PhamePostController',
|
'PhamePostHistoryController' => 'PhamePostController',
|
||||||
'PhamePostListController' => 'PhamePostController',
|
'PhamePostListController' => 'PhamePostController',
|
||||||
'PhamePostListView' => 'AphrontTagView',
|
'PhamePostListView' => 'AphrontTagView',
|
||||||
|
|
|
@ -542,7 +542,7 @@ final class PhabricatorAuditEditor
|
||||||
protected function expandCustomRemarkupBlockTransactions(
|
protected function expandCustomRemarkupBlockTransactions(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions,
|
array $xactions,
|
||||||
$blocks,
|
array $changes,
|
||||||
PhutilMarkupEngine $engine) {
|
PhutilMarkupEngine $engine) {
|
||||||
|
|
||||||
// we are only really trying to find unmentionable phids here...
|
// we are only really trying to find unmentionable phids here...
|
||||||
|
@ -563,7 +563,7 @@ final class PhabricatorAuditEditor
|
||||||
return $result;
|
return $result;
|
||||||
}
|
}
|
||||||
|
|
||||||
$flat_blocks = array_mergev($blocks);
|
$flat_blocks = mpull($changes, 'getNewValue');
|
||||||
$huge_block = implode("\n\n", $flat_blocks);
|
$huge_block = implode("\n\n", $flat_blocks);
|
||||||
$phid_map = array();
|
$phid_map = array();
|
||||||
$phid_map[] = $this->getUnmentionablePHIDMap();
|
$phid_map[] = $this->getUnmentionablePHIDMap();
|
||||||
|
|
|
@ -30,9 +30,11 @@ final class PhabricatorAuthAuthProviderPHIDType extends PhabricatorPHIDType {
|
||||||
array $objects) {
|
array $objects) {
|
||||||
|
|
||||||
foreach ($handles as $phid => $handle) {
|
foreach ($handles as $phid => $handle) {
|
||||||
$provider = $objects[$phid];
|
$provider = $objects[$phid]->getProvider();
|
||||||
|
|
||||||
$handle->setName($provider->getProviderName());
|
if ($provider) {
|
||||||
|
$handle->setName($provider->getProviderName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -190,6 +190,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||||
'The Differential revision list view age UI elements have been removed '.
|
'The Differential revision list view age UI elements have been removed '.
|
||||||
'to simplify the interface.');
|
'to simplify the interface.');
|
||||||
|
|
||||||
|
$global_settings_reason = pht(
|
||||||
|
'The "Re: Prefix" and "Vary Subjects" settings are now configured '.
|
||||||
|
'in global settings.');
|
||||||
|
|
||||||
$ancient_config += array(
|
$ancient_config += array(
|
||||||
'phid.external-loaders' =>
|
'phid.external-loaders' =>
|
||||||
pht(
|
pht(
|
||||||
|
@ -321,6 +325,9 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck {
|
||||||
|
|
||||||
'differential.days-fresh' => $stale_reason,
|
'differential.days-fresh' => $stale_reason,
|
||||||
'differential.days-stale' => $stale_reason,
|
'differential.days-stale' => $stale_reason,
|
||||||
|
|
||||||
|
'metamta.re-prefix' => $global_settings_reason,
|
||||||
|
'metamta.vary-subjects' => $global_settings_reason,
|
||||||
);
|
);
|
||||||
|
|
||||||
return $ancient_config;
|
return $ancient_config;
|
||||||
|
|
|
@ -276,22 +276,6 @@ EODOC
|
||||||
))
|
))
|
||||||
->setSummary(pht('Show email preferences link in email.'))
|
->setSummary(pht('Show email preferences link in email.'))
|
||||||
->setDescription($email_preferences_description),
|
->setDescription($email_preferences_description),
|
||||||
$this->newOption('metamta.re-prefix', 'bool', false)
|
|
||||||
->setBoolOptions(
|
|
||||||
array(
|
|
||||||
pht('Force "Re:" Subject Prefix'),
|
|
||||||
pht('No "Re:" Subject Prefix'),
|
|
||||||
))
|
|
||||||
->setSummary(pht('Control "Re:" subject prefix, for Mail.app.'))
|
|
||||||
->setDescription($re_prefix_description),
|
|
||||||
$this->newOption('metamta.vary-subjects', 'bool', true)
|
|
||||||
->setBoolOptions(
|
|
||||||
array(
|
|
||||||
pht('Allow Varied Subjects'),
|
|
||||||
pht('Always Use the Same Thread Subject'),
|
|
||||||
))
|
|
||||||
->setSummary(pht('Control subject variance, for some mail clients.'))
|
|
||||||
->setDescription($vary_subjects_description),
|
|
||||||
$this->newOption('metamta.insecure-auth-with-reply-to', 'bool', false)
|
$this->newOption('metamta.insecure-auth-with-reply-to', 'bool', false)
|
||||||
->setBoolOptions(
|
->setBoolOptions(
|
||||||
array(
|
array(
|
||||||
|
|
|
@ -43,6 +43,14 @@ final class PhabricatorSecurityConfigOptions
|
||||||
'255.255.255.255/32',
|
'255.255.255.255/32',
|
||||||
);
|
);
|
||||||
|
|
||||||
|
$keyring_type = 'custom:PhabricatorKeyringConfigOptionType';
|
||||||
|
$keyring_description = $this->deformat(pht(<<<EOTEXT
|
||||||
|
The keyring stores master encryption keys. For help with configuring a keyring
|
||||||
|
and encryption, see **[[ %s | Configuring Encryption ]]**.
|
||||||
|
EOTEXT
|
||||||
|
,
|
||||||
|
PhabricatorEnv::getDoclink('Configuring Encryption')));
|
||||||
|
|
||||||
return array(
|
return array(
|
||||||
$this->newOption('security.alternate-file-domain', 'string', null)
|
$this->newOption('security.alternate-file-domain', 'string', null)
|
||||||
->setLocked(true)
|
->setLocked(true)
|
||||||
|
@ -276,6 +284,10 @@ final class PhabricatorSecurityConfigOptions
|
||||||
'unsecured content over plain HTTP. It is very difficult to '.
|
'unsecured content over plain HTTP. It is very difficult to '.
|
||||||
'undo this change once users\' browsers have accepted the '.
|
'undo this change once users\' browsers have accepted the '.
|
||||||
'setting.')),
|
'setting.')),
|
||||||
|
$this->newOption('keyring', $keyring_type, array())
|
||||||
|
->setHidden(true)
|
||||||
|
->setSummary(pht('Configure master encryption keys.'))
|
||||||
|
->setDescription($keyring_description),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1303,10 +1303,10 @@ final class DifferentialTransactionEditor
|
||||||
protected function expandCustomRemarkupBlockTransactions(
|
protected function expandCustomRemarkupBlockTransactions(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions,
|
array $xactions,
|
||||||
$blocks,
|
array $changes,
|
||||||
PhutilMarkupEngine $engine) {
|
PhutilMarkupEngine $engine) {
|
||||||
|
|
||||||
$flat_blocks = array_mergev($blocks);
|
$flat_blocks = mpull($changes, 'getNewValue');
|
||||||
$huge_block = implode("\n\n", $flat_blocks);
|
$huge_block = implode("\n\n", $flat_blocks);
|
||||||
|
|
||||||
$task_map = array();
|
$task_map = array();
|
||||||
|
|
|
@ -75,7 +75,7 @@ final class DifferentialRevisionSearchEngine
|
||||||
->setLabel(pht('Repositories'))
|
->setLabel(pht('Repositories'))
|
||||||
->setKey('repositoryPHIDs')
|
->setKey('repositoryPHIDs')
|
||||||
->setAliases(array('repository', 'repositories', 'repositoryPHID'))
|
->setAliases(array('repository', 'repositories', 'repositoryPHID'))
|
||||||
->setDatasource(new DiffusionRepositoryDatasource())
|
->setDatasource(new DifferentialRepositoryDatasource())
|
||||||
->setDescription(
|
->setDescription(
|
||||||
pht('Find revisions from specific repositories.')),
|
pht('Find revisions from specific repositories.')),
|
||||||
id(new PhabricatorSearchSelectField())
|
id(new PhabricatorSearchSelectField())
|
||||||
|
|
|
@ -0,0 +1,25 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DifferentialRepositoryDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Repositories');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type a repository name or function...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorDifferentialApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new DiffusionTaggedRepositoriesFunctionDatasource(),
|
||||||
|
new DiffusionRepositoryDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -129,6 +129,12 @@ final class DiffusionCommitController extends DiffusionController {
|
||||||
),
|
),
|
||||||
$message));
|
$message));
|
||||||
|
|
||||||
|
if ($commit->isUnreachable()) {
|
||||||
|
$this->commitErrors[] = pht(
|
||||||
|
'This commit has been deleted in the repository: it is no longer '.
|
||||||
|
'reachable from any branch, tag, or ref.');
|
||||||
|
}
|
||||||
|
|
||||||
if ($this->getCommitErrors()) {
|
if ($this->getCommitErrors()) {
|
||||||
$error_panel = id(new PHUIInfoView())
|
$error_panel = id(new PHUIInfoView())
|
||||||
->appendChild($this->getCommitErrors())
|
->appendChild($this->getCommitErrors())
|
||||||
|
|
|
@ -113,9 +113,7 @@ final class DiffusionPathChange extends Phobject {
|
||||||
if (!$this->getCommitData()) {
|
if (!$this->getCommitData()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
$message = $this->getCommitData()->getCommitMessage();
|
return $this->getCommitData()->getSummary();
|
||||||
$first = idx(explode("\n", $message), 0);
|
|
||||||
return substr($first, 0, 80);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function convertToArcanistChanges(array $changes) {
|
public static function convertToArcanistChanges(array $changes) {
|
||||||
|
|
|
@ -7,6 +7,7 @@ final class DiffusionRepositoryRef extends Phobject {
|
||||||
|
|
||||||
private $shortName;
|
private $shortName;
|
||||||
private $commitIdentifier;
|
private $commitIdentifier;
|
||||||
|
private $refType;
|
||||||
private $rawFields = array();
|
private $rawFields = array();
|
||||||
|
|
||||||
public function setRawFields(array $raw_fields) {
|
public function setRawFields(array $raw_fields) {
|
||||||
|
@ -36,6 +37,25 @@ final class DiffusionRepositoryRef extends Phobject {
|
||||||
return $this->shortName;
|
return $this->shortName;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setRefType($ref_type) {
|
||||||
|
$this->refType = $ref_type;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getRefType() {
|
||||||
|
return $this->refType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isBranch() {
|
||||||
|
$type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH;
|
||||||
|
return ($this->getRefType() === $type_branch);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function isTag() {
|
||||||
|
$type_tag = PhabricatorRepositoryRefCursor::TYPE_TAG;
|
||||||
|
return ($this->getRefType() === $type_tag);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( Serialization )------------------------------------------------------ */
|
/* -( Serialization )------------------------------------------------------ */
|
||||||
|
|
||||||
|
@ -44,6 +64,7 @@ final class DiffusionRepositoryRef extends Phobject {
|
||||||
return array(
|
return array(
|
||||||
'shortName' => $this->shortName,
|
'shortName' => $this->shortName,
|
||||||
'commitIdentifier' => $this->commitIdentifier,
|
'commitIdentifier' => $this->commitIdentifier,
|
||||||
|
'refType' => $this->refType,
|
||||||
'rawFields' => $this->rawFields,
|
'rawFields' => $this->rawFields,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -52,6 +73,7 @@ final class DiffusionRepositoryRef extends Phobject {
|
||||||
return id(new DiffusionRepositoryRef())
|
return id(new DiffusionRepositoryRef())
|
||||||
->setShortName($dict['shortName'])
|
->setShortName($dict['shortName'])
|
||||||
->setCommitIdentifier($dict['commitIdentifier'])
|
->setCommitIdentifier($dict['commitIdentifier'])
|
||||||
|
->setRefType($dict['refType'])
|
||||||
->setRawFields($dict['rawFields']);
|
->setRawFields($dict['rawFields']);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,96 +14,121 @@ final class DiffusionLowLevelGitRefQuery extends DiffusionLowLevelQuery {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function executeQuery() {
|
protected function executeQuery() {
|
||||||
|
$type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH;
|
||||||
|
$type_tag = PhabricatorRepositoryRefCursor::TYPE_TAG;
|
||||||
|
$type_ref = PhabricatorRepositoryRefCursor::TYPE_REF;
|
||||||
|
|
||||||
$ref_types = $this->refTypes;
|
$ref_types = $this->refTypes;
|
||||||
if ($ref_types) {
|
if (!$ref_types) {
|
||||||
$type_branch = PhabricatorRepositoryRefCursor::TYPE_BRANCH;
|
$ref_types = array($type_branch, $type_tag, $type_ref);
|
||||||
$type_tag = PhabricatorRepositoryRefCursor::TYPE_TAG;
|
|
||||||
|
|
||||||
$ref_types = array_fuse($ref_types);
|
|
||||||
|
|
||||||
$with_branches = isset($ref_types[$type_branch]);
|
|
||||||
$with_tags = isset($ref_types[$type_tag]);
|
|
||||||
} else {
|
|
||||||
$with_branches = true;
|
|
||||||
$with_tags = true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$ref_types = array_fuse($ref_types);
|
||||||
|
|
||||||
|
$with_branches = isset($ref_types[$type_branch]);
|
||||||
|
$with_tags = isset($ref_types[$type_tag]);
|
||||||
|
$with_refs = isset($refs_types[$type_ref]);
|
||||||
|
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
$prefixes = array();
|
$prefixes = array();
|
||||||
|
|
||||||
if ($with_branches) {
|
if ($repository->isWorkingCopyBare()) {
|
||||||
if ($repository->isWorkingCopyBare()) {
|
$branch_prefix = 'refs/heads/';
|
||||||
$prefix = 'refs/heads/';
|
} else {
|
||||||
} else {
|
$remote = DiffusionGitBranch::DEFAULT_GIT_REMOTE;
|
||||||
$remote = DiffusionGitBranch::DEFAULT_GIT_REMOTE;
|
$branch_prefix = 'refs/remotes/'.$remote.'/';
|
||||||
$prefix = 'refs/remotes/'.$remote.'/';
|
}
|
||||||
|
|
||||||
|
$tag_prefix = 'refs/tags/';
|
||||||
|
|
||||||
|
|
||||||
|
if ($with_refs || count($ref_types) > 1) {
|
||||||
|
// If we're loading refs or more than one type of ref, just query
|
||||||
|
// everything.
|
||||||
|
$prefix = 'refs/';
|
||||||
|
} else {
|
||||||
|
if ($with_branches) {
|
||||||
|
$prefix = $branch_prefix;
|
||||||
|
}
|
||||||
|
if ($with_tags) {
|
||||||
|
$prefix = $tag_prefix;
|
||||||
}
|
}
|
||||||
$prefixes[] = $prefix;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($with_tags) {
|
$branch_len = strlen($branch_prefix);
|
||||||
$prefixes[] = 'refs/tags/';
|
$tag_len = strlen($tag_prefix);
|
||||||
|
|
||||||
|
list($stdout) = $repository->execxLocalCommand(
|
||||||
|
'for-each-ref --sort=%s --format=%s -- %s',
|
||||||
|
'-creatordate',
|
||||||
|
$this->getFormatString(),
|
||||||
|
$prefix);
|
||||||
|
|
||||||
|
$stdout = rtrim($stdout);
|
||||||
|
if (!strlen($stdout)) {
|
||||||
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$order = '-creatordate';
|
$remote_prefix = 'refs/remotes/';
|
||||||
|
$remote_len = strlen($remote_prefix);
|
||||||
|
|
||||||
$futures = array();
|
// NOTE: Although git supports --count, we can't apply any offset or
|
||||||
foreach ($prefixes as $prefix) {
|
// limit logic until the very end because we may encounter a HEAD which
|
||||||
$futures[$prefix] = $repository->getLocalCommandFuture(
|
// we want to discard.
|
||||||
'for-each-ref --sort=%s --format=%s %s',
|
|
||||||
$order,
|
|
||||||
$this->getFormatString(),
|
|
||||||
$prefix);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resolve all the futures first. We want to iterate over them in prefix
|
|
||||||
// order, not resolution order.
|
|
||||||
foreach (new FutureIterator($futures) as $prefix => $future) {
|
|
||||||
$future->resolvex();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
$lines = explode("\n", $stdout);
|
||||||
$results = array();
|
$results = array();
|
||||||
foreach ($futures as $prefix => $future) {
|
foreach ($lines as $line) {
|
||||||
list($stdout) = $future->resolvex();
|
$fields = $this->extractFields($line);
|
||||||
|
|
||||||
$stdout = rtrim($stdout);
|
$refname = $fields['refname'];
|
||||||
if (!strlen($stdout)) {
|
if (!strncmp($refname, $branch_prefix, $branch_len)) {
|
||||||
|
$short = substr($refname, $branch_len);
|
||||||
|
$type = $type_branch;
|
||||||
|
} else if (!strncmp($refname, $tag_prefix, $tag_len)) {
|
||||||
|
$short = substr($refname, $tag_len);
|
||||||
|
$type = $type_tag;
|
||||||
|
} else if (!strncmp($refname, $remote_prefix, $remote_len)) {
|
||||||
|
// If we've found a remote ref that we didn't recognize as naming a
|
||||||
|
// branch, just ignore it. This can happen if we're observing a remote,
|
||||||
|
// and that remote has its own remotes. We don't care about their
|
||||||
|
// state and they may be out of date, so ignore them.
|
||||||
|
continue;
|
||||||
|
} else {
|
||||||
|
$short = $refname;
|
||||||
|
$type = $type_ref;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If this isn't a type of ref we care about, skip it.
|
||||||
|
if (empty($ref_types[$type])) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
// NOTE: Although git supports --count, we can't apply any offset or
|
// If this is the local HEAD, skip it.
|
||||||
// limit logic until the very end because we may encounter a HEAD which
|
if ($short == 'HEAD') {
|
||||||
// we want to discard.
|
continue;
|
||||||
|
|
||||||
$lines = explode("\n", $stdout);
|
|
||||||
foreach ($lines as $line) {
|
|
||||||
$fields = $this->extractFields($line);
|
|
||||||
|
|
||||||
$creator = $fields['creator'];
|
|
||||||
$matches = null;
|
|
||||||
if (preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) {
|
|
||||||
$fields['author'] = $matches[1];
|
|
||||||
$fields['epoch'] = (int)$matches[2];
|
|
||||||
} else {
|
|
||||||
$fields['author'] = null;
|
|
||||||
$fields['epoch'] = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
$commit = nonempty($fields['*objectname'], $fields['objectname']);
|
|
||||||
|
|
||||||
$short = substr($fields['refname'], strlen($prefix));
|
|
||||||
if ($short == 'HEAD') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
$ref = id(new DiffusionRepositoryRef())
|
|
||||||
->setShortName($short)
|
|
||||||
->setCommitIdentifier($commit)
|
|
||||||
->setRawFields($fields);
|
|
||||||
|
|
||||||
$results[] = $ref;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$creator = $fields['creator'];
|
||||||
|
$matches = null;
|
||||||
|
if (preg_match('/^(.*) ([0-9]+) ([0-9+-]+)$/', $creator, $matches)) {
|
||||||
|
$fields['author'] = $matches[1];
|
||||||
|
$fields['epoch'] = (int)$matches[2];
|
||||||
|
} else {
|
||||||
|
$fields['author'] = null;
|
||||||
|
$fields['epoch'] = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit = nonempty($fields['*objectname'], $fields['objectname']);
|
||||||
|
|
||||||
|
$ref = id(new DiffusionRepositoryRef())
|
||||||
|
->setRefType($type)
|
||||||
|
->setShortName($short)
|
||||||
|
->setCommitIdentifier($commit)
|
||||||
|
->setRawFields($fields);
|
||||||
|
|
||||||
|
$results[] = $ref;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $results;
|
return $results;
|
||||||
|
|
|
@ -0,0 +1,100 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class DiffusionTaggedRepositoriesFunctionDatasource
|
||||||
|
extends PhabricatorTypeaheadCompositeDatasource {
|
||||||
|
|
||||||
|
public function getBrowseTitle() {
|
||||||
|
return pht('Browse Repositories');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPlaceholderText() {
|
||||||
|
return pht('Type tagged(<project>)...');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceApplicationClass() {
|
||||||
|
return 'PhabricatorProjectApplication';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getComponentDatasources() {
|
||||||
|
return array(
|
||||||
|
new PhabricatorProjectDatasource(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDatasourceFunctions() {
|
||||||
|
return array(
|
||||||
|
'tagged' => array(
|
||||||
|
'name' => pht('Repositories: ...'),
|
||||||
|
'arguments' => pht('project'),
|
||||||
|
'summary' => pht('Find results for repositories of a project.'),
|
||||||
|
'description' => pht(
|
||||||
|
'This function allows you to find results for any of the `.
|
||||||
|
`repositories of a project:'.
|
||||||
|
"\n\n".
|
||||||
|
'> tagged(engineering)'),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function didLoadResults(array $results) {
|
||||||
|
foreach ($results as $result) {
|
||||||
|
$result
|
||||||
|
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||||
|
->setColor(null)
|
||||||
|
->setPHID('tagged('.$result->getPHID().')')
|
||||||
|
->setDisplayName(pht('Tagged: %s', $result->getDisplayName()))
|
||||||
|
->setName('tagged '.$result->getName());
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function evaluateFunction($function, array $argv_list) {
|
||||||
|
$phids = array();
|
||||||
|
foreach ($argv_list as $argv) {
|
||||||
|
$phids[] = head($argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
$repositories = id(new PhabricatorRepositoryQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withEdgeLogicPHIDs(
|
||||||
|
PhabricatorProjectObjectHasProjectEdgeType::EDGECONST,
|
||||||
|
PhabricatorQueryConstraint::OPERATOR_OR,
|
||||||
|
$phids)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$results = array();
|
||||||
|
|
||||||
|
foreach ($repositories as $repository) {
|
||||||
|
$results[] = $repository->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
return $results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function renderFunctionTokens($function, array $argv_list) {
|
||||||
|
$phids = array();
|
||||||
|
foreach ($argv_list as $argv) {
|
||||||
|
$phids[] = head($argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
$tokens = $this->renderTokens($phids);
|
||||||
|
foreach ($tokens as $token) {
|
||||||
|
// Remove any project color on this token.
|
||||||
|
$token->setColor(null);
|
||||||
|
|
||||||
|
if ($token->isInvalid()) {
|
||||||
|
$token
|
||||||
|
->setValue(pht('Repositories: Invalid Project'));
|
||||||
|
} else {
|
||||||
|
$token
|
||||||
|
->setTokenType(PhabricatorTypeaheadTokenView::TYPE_FUNCTION)
|
||||||
|
->setKey('tagged('.$token->getKey().')')
|
||||||
|
->setValue(pht('Tagged: %s', $token->getValue()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $tokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -215,6 +215,7 @@ final class PhabricatorFeedStoryPublisher extends Phobject {
|
||||||
$all_prefs = id(new PhabricatorUserPreferencesQuery())
|
$all_prefs = id(new PhabricatorUserPreferencesQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
->withUserPHIDs($phids)
|
->withUserPHIDs($phids)
|
||||||
|
->needSyntheticPreferences(true)
|
||||||
->execute();
|
->execute();
|
||||||
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
|
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
|
||||||
}
|
}
|
||||||
|
|
|
@ -44,6 +44,7 @@ final class PhabricatorFilesConfigOptions
|
||||||
'video/mp4' => 'video/mp4',
|
'video/mp4' => 'video/mp4',
|
||||||
'video/ogg' => 'video/ogg',
|
'video/ogg' => 'video/ogg',
|
||||||
'video/webm' => 'video/webm',
|
'video/webm' => 'video/webm',
|
||||||
|
'video/quicktime' => 'video/quicktime',
|
||||||
);
|
);
|
||||||
|
|
||||||
$image_default = array(
|
$image_default = array(
|
||||||
|
@ -71,6 +72,7 @@ final class PhabricatorFilesConfigOptions
|
||||||
// to set the mood for your task without distracting viewers.)
|
// to set the mood for your task without distracting viewers.)
|
||||||
'video/mp4' => true,
|
'video/mp4' => true,
|
||||||
'video/ogg' => true,
|
'video/ogg' => true,
|
||||||
|
'video/quicktime' => true,
|
||||||
'application/ogg' => true,
|
'application/ogg' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -78,6 +80,7 @@ final class PhabricatorFilesConfigOptions
|
||||||
'video/mp4' => true,
|
'video/mp4' => true,
|
||||||
'video/ogg' => true,
|
'video/ogg' => true,
|
||||||
'video/webm' => true,
|
'video/webm' => true,
|
||||||
|
'video/quicktime' => true,
|
||||||
'application/ogg' => true,
|
'application/ogg' => true,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -256,8 +256,10 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
||||||
$types[] = pht('Profile');
|
$types[] = pht('Profile');
|
||||||
}
|
}
|
||||||
|
|
||||||
$types = implode(', ', $types);
|
if ($types) {
|
||||||
$finfo->addProperty(pht('Attributes'), $types);
|
$types = implode(', ', $types);
|
||||||
|
$finfo->addProperty(pht('Attributes'), $types);
|
||||||
|
}
|
||||||
|
|
||||||
$storage_properties = new PHUIPropertyListView();
|
$storage_properties = new PHUIPropertyListView();
|
||||||
$box->addPropertyList($storage_properties, pht('Storage'));
|
$box->addPropertyList($storage_properties, pht('Storage'));
|
||||||
|
@ -266,9 +268,14 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
||||||
pht('Engine'),
|
pht('Engine'),
|
||||||
$file->getStorageEngine());
|
$file->getStorageEngine());
|
||||||
|
|
||||||
$storage_properties->addProperty(
|
$format_key = $file->getStorageFormat();
|
||||||
pht('Format'),
|
$format = PhabricatorFileStorageFormat::getFormat($format_key);
|
||||||
$file->getStorageFormat());
|
if ($format) {
|
||||||
|
$format_name = $format->getStorageFormatName();
|
||||||
|
} else {
|
||||||
|
$format_name = pht('Unknown ("%s")', $format_key);
|
||||||
|
}
|
||||||
|
$storage_properties->addProperty(pht('Format'), $format_name);
|
||||||
|
|
||||||
$storage_properties->addProperty(
|
$storage_properties->addProperty(
|
||||||
pht('Handle'),
|
pht('Handle'),
|
||||||
|
|
|
@ -174,7 +174,7 @@ final class PhabricatorChunkedFileStorageEngine
|
||||||
return (4 * 1024 * 1024);
|
return (4 * 1024 * 1024);
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
public function getRawFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
||||||
$chunks = id(new PhabricatorFileChunkQuery())
|
$chunks = id(new PhabricatorFileChunkQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
->withChunkHandles(array($file->getStorageHandle()))
|
->withChunkHandles(array($file->getStorageHandle()))
|
||||||
|
|
|
@ -325,10 +325,10 @@ abstract class PhabricatorFileStorageEngine extends Phobject {
|
||||||
return $engine->getChunkSize();
|
return $engine->getChunkSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
public function getRawFileDataIterator(PhabricatorFile $file, $begin, $end) {
|
||||||
// The default implementation is trivial and just loads the entire file
|
// The default implementation is trivial and just loads the entire file
|
||||||
// upfront.
|
// upfront.
|
||||||
$data = $file->loadFileData();
|
$data = $this->readFile($file->getStorageHandle());
|
||||||
|
|
||||||
if ($begin !== null && $end !== null) {
|
if ($begin !== null && $end !== null) {
|
||||||
$data = substr($data, $begin, ($end - $begin));
|
$data = substr($data, $begin, ($end - $begin));
|
||||||
|
|
|
@ -0,0 +1,199 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* At-rest encryption format using AES256 CBC.
|
||||||
|
*/
|
||||||
|
final class PhabricatorFileAES256StorageFormat
|
||||||
|
extends PhabricatorFileStorageFormat {
|
||||||
|
|
||||||
|
const FORMATKEY = 'aes-256-cbc';
|
||||||
|
|
||||||
|
private $keyName;
|
||||||
|
|
||||||
|
public function getStorageFormatName() {
|
||||||
|
return pht('Encrypted (AES-256-CBC)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canGenerateNewKeyMaterial() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewKeyMaterial() {
|
||||||
|
$envelope = self::newAES256Key();
|
||||||
|
$material = $envelope->openEnvelope();
|
||||||
|
return base64_encode($material);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canCycleMasterKey() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cycleStorageProperties() {
|
||||||
|
$file = $this->getFile();
|
||||||
|
list($key, $iv) = $this->extractKeyAndIV($file);
|
||||||
|
return $this->formatStorageProperties($key, $iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newReadIterator($raw_iterator) {
|
||||||
|
$file = $this->getFile();
|
||||||
|
$data = $file->loadDataFromIterator($raw_iterator);
|
||||||
|
|
||||||
|
list($key, $iv) = $this->extractKeyAndIV($file);
|
||||||
|
|
||||||
|
$data = $this->decryptData($data, $key, $iv);
|
||||||
|
|
||||||
|
return array($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newWriteIterator($raw_iterator) {
|
||||||
|
$file = $this->getFile();
|
||||||
|
$data = $file->loadDataFromIterator($raw_iterator);
|
||||||
|
|
||||||
|
list($key, $iv) = $this->extractKeyAndIV($file);
|
||||||
|
|
||||||
|
$data = $this->encryptData($data, $key, $iv);
|
||||||
|
|
||||||
|
return array($data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newStorageProperties() {
|
||||||
|
// Generate a unique key and IV for this block of data.
|
||||||
|
$key_envelope = self::newAES256Key();
|
||||||
|
$iv_envelope = self::newAES256IV();
|
||||||
|
|
||||||
|
return $this->formatStorageProperties($key_envelope, $iv_envelope);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function formatStorageProperties(
|
||||||
|
PhutilOpaqueEnvelope $key_envelope,
|
||||||
|
PhutilOpaqueEnvelope $iv_envelope) {
|
||||||
|
|
||||||
|
// Encode the raw binary data with base64 so we can wrap it in JSON.
|
||||||
|
$data = array(
|
||||||
|
'iv.base64' => base64_encode($iv_envelope->openEnvelope()),
|
||||||
|
'key.base64' => base64_encode($key_envelope->openEnvelope()),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Encode the base64 data with JSON.
|
||||||
|
$data_clear = phutil_json_encode($data);
|
||||||
|
|
||||||
|
// Encrypt the block key with the master key, using a unique IV.
|
||||||
|
$data_iv = self::newAES256IV();
|
||||||
|
$key_name = $this->getMasterKeyName();
|
||||||
|
$master_key = $this->getMasterKeyMaterial($key_name);
|
||||||
|
$data_cipher = $this->encryptData($data_clear, $master_key, $data_iv);
|
||||||
|
|
||||||
|
return array(
|
||||||
|
'key.name' => $key_name,
|
||||||
|
'iv.base64' => base64_encode($data_iv->openEnvelope()),
|
||||||
|
'payload.base64' => base64_encode($data_cipher),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function extractKeyAndIV(PhabricatorFile $file) {
|
||||||
|
$outer_iv = $file->getStorageProperty('iv.base64');
|
||||||
|
$outer_iv = base64_decode($outer_iv);
|
||||||
|
$outer_iv = new PhutilOpaqueEnvelope($outer_iv);
|
||||||
|
|
||||||
|
$outer_payload = $file->getStorageProperty('payload.base64');
|
||||||
|
$outer_payload = base64_decode($outer_payload);
|
||||||
|
|
||||||
|
$outer_key_name = $file->getStorageProperty('key.name');
|
||||||
|
$outer_key = $this->getMasterKeyMaterial($outer_key_name);
|
||||||
|
|
||||||
|
$payload = $this->decryptData($outer_payload, $outer_key, $outer_iv);
|
||||||
|
$payload = phutil_json_decode($payload);
|
||||||
|
|
||||||
|
$inner_iv = $payload['iv.base64'];
|
||||||
|
$inner_iv = base64_decode($inner_iv);
|
||||||
|
$inner_iv = new PhutilOpaqueEnvelope($inner_iv);
|
||||||
|
|
||||||
|
$inner_key = $payload['key.base64'];
|
||||||
|
$inner_key = base64_decode($inner_key);
|
||||||
|
$inner_key = new PhutilOpaqueEnvelope($inner_key);
|
||||||
|
|
||||||
|
return array($inner_key, $inner_iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function encryptData(
|
||||||
|
$data,
|
||||||
|
PhutilOpaqueEnvelope $key,
|
||||||
|
PhutilOpaqueEnvelope $iv) {
|
||||||
|
|
||||||
|
$method = 'aes-256-cbc';
|
||||||
|
$key = $key->openEnvelope();
|
||||||
|
$iv = $iv->openEnvelope();
|
||||||
|
|
||||||
|
$result = openssl_encrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
|
||||||
|
if ($result === false) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Failed to openssl_encrypt() data: %s',
|
||||||
|
openssl_error_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function decryptData(
|
||||||
|
$data,
|
||||||
|
PhutilOpaqueEnvelope $key,
|
||||||
|
PhutilOpaqueEnvelope $iv) {
|
||||||
|
|
||||||
|
$method = 'aes-256-cbc';
|
||||||
|
$key = $key->openEnvelope();
|
||||||
|
$iv = $iv->openEnvelope();
|
||||||
|
|
||||||
|
$result = openssl_decrypt($data, $method, $key, OPENSSL_RAW_DATA, $iv);
|
||||||
|
if ($result === false) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Failed to openssl_decrypt() data: %s',
|
||||||
|
openssl_error_string()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newAES256Key() {
|
||||||
|
// Unsurprisingly, AES256 uses a 256 bit key.
|
||||||
|
$key = Filesystem::readRandomBytes(phutil_units('256 bits in bytes'));
|
||||||
|
return new PhutilOpaqueEnvelope($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function newAES256IV() {
|
||||||
|
// AES256 uses a 256 bit key, but the initialization vector length is
|
||||||
|
// only 128 bits.
|
||||||
|
$iv = Filesystem::readRandomBytes(phutil_units('128 bits in bytes'));
|
||||||
|
return new PhutilOpaqueEnvelope($iv);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectMasterKey($key_name) {
|
||||||
|
// Require that the key exist on the key ring.
|
||||||
|
$this->getMasterKeyMaterial($key_name);
|
||||||
|
|
||||||
|
$this->keyName = $key_name;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMasterKeyName() {
|
||||||
|
if ($this->keyName !== null) {
|
||||||
|
return $this->keyName;
|
||||||
|
}
|
||||||
|
|
||||||
|
$default = PhabricatorKeyring::getDefaultKeyName(self::FORMATKEY);
|
||||||
|
if ($default !== null) {
|
||||||
|
return $default;
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'No AES256 key is specified in the keyring as a default encryption '.
|
||||||
|
'key, and no encryption key has been explicitly selected.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
private function getMasterKeyMaterial($key_name) {
|
||||||
|
return PhabricatorKeyring::getKey($key_name, self::FORMATKEY);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,44 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Trivial example of a file storage format for at-rest encryption.
|
||||||
|
*
|
||||||
|
* This format applies ROT13 encoding to file data as it is stored and
|
||||||
|
* reverses it on the way out. This encoding is trivially reversible. This
|
||||||
|
* format is for testing, developing, and understanding encoding formats and
|
||||||
|
* is not intended for production use.
|
||||||
|
*/
|
||||||
|
final class PhabricatorFileROT13StorageFormat
|
||||||
|
extends PhabricatorFileStorageFormat {
|
||||||
|
|
||||||
|
const FORMATKEY = 'rot13';
|
||||||
|
|
||||||
|
public function getStorageFormatName() {
|
||||||
|
return pht('Encoded (ROT13)');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newReadIterator($raw_iterator) {
|
||||||
|
$file = $this->getFile();
|
||||||
|
$iterations = $file->getStorageProperty('iterations', 1);
|
||||||
|
|
||||||
|
$value = $file->loadDataFromIterator($raw_iterator);
|
||||||
|
for ($ii = 0; $ii < $iterations; $ii++) {
|
||||||
|
$value = str_rot13($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return array($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newWriteIterator($raw_iterator) {
|
||||||
|
return $this->newReadIterator($raw_iterator);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newStorageProperties() {
|
||||||
|
// For extreme security, repeatedly encode the data using a random (odd)
|
||||||
|
// number of iterations.
|
||||||
|
return array(
|
||||||
|
'iterations' => (mt_rand(1, 3) * 2) - 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,20 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFileRawStorageFormat
|
||||||
|
extends PhabricatorFileStorageFormat {
|
||||||
|
|
||||||
|
const FORMATKEY = 'raw';
|
||||||
|
|
||||||
|
public function getStorageFormatName() {
|
||||||
|
return pht('Raw Data');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newReadIterator($raw_iterator) {
|
||||||
|
return $raw_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newWriteIterator($raw_iterator) {
|
||||||
|
return $raw_iterator;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorFileStorageFormat
|
||||||
|
extends Phobject {
|
||||||
|
|
||||||
|
private $file;
|
||||||
|
|
||||||
|
final public function setFile(PhabricatorFile $file) {
|
||||||
|
$this->file = $file;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getFile() {
|
||||||
|
if (!$this->file) {
|
||||||
|
throw new PhutilInvalidStateException('setFile');
|
||||||
|
}
|
||||||
|
return $this->file;
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract public function getStorageFormatName();
|
||||||
|
|
||||||
|
abstract public function newReadIterator($raw_iterator);
|
||||||
|
abstract public function newWriteIterator($raw_iterator);
|
||||||
|
|
||||||
|
public function newStorageProperties() {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canGenerateNewKeyMaterial() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewKeyMaterial() {
|
||||||
|
throw new PhutilMethodNotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canCycleMasterKey() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cycleStorageProperties() {
|
||||||
|
throw new PhutilMethodNotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function selectMasterKey($key_name) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'This storage format ("%s") does not support key selection.',
|
||||||
|
$this->getStorageFormatName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
final public function getStorageFormatKey() {
|
||||||
|
return $this->getPhobjectClassConstant('FORMATKEY');
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getAllFormats() {
|
||||||
|
return id(new PhutilClassMapQuery())
|
||||||
|
->setAncestorClass(__CLASS__)
|
||||||
|
->setUniqueMethod('getStorageFormatKey')
|
||||||
|
->execute();
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function getFormat($key) {
|
||||||
|
$formats = self::getAllFormats();
|
||||||
|
return idx($formats, $key);
|
||||||
|
}
|
||||||
|
|
||||||
|
final public static function requireFormat($key) {
|
||||||
|
$format = self::getFormat($key);
|
||||||
|
|
||||||
|
if (!$format) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'No file storage format with key "%s" exists.',
|
||||||
|
$key));
|
||||||
|
}
|
||||||
|
|
||||||
|
return $format;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,77 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFileStorageFormatTestCase extends PhabricatorTestCase {
|
||||||
|
|
||||||
|
protected function getPhabricatorTestCaseConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::PHABRICATOR_TESTCONFIG_BUILD_STORAGE_FIXTURES => true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testRot13Storage() {
|
||||||
|
$engine = new PhabricatorTestStorageEngine();
|
||||||
|
$rot13_format = PhabricatorFileROT13StorageFormat::FORMATKEY;
|
||||||
|
|
||||||
|
$data = 'The cow jumped over the full moon.';
|
||||||
|
$expect = 'Gur pbj whzcrq bire gur shyy zbba.';
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'name' => 'test.dat',
|
||||||
|
'storageEngines' => array(
|
||||||
|
$engine,
|
||||||
|
),
|
||||||
|
'format' => $rot13_format,
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = PhabricatorFile::newFromFileData($data, $params);
|
||||||
|
|
||||||
|
// We should have a file stored as rot13, which reads back the input
|
||||||
|
// data correctly.
|
||||||
|
$this->assertEqual($rot13_format, $file->getStorageFormat());
|
||||||
|
$this->assertEqual($data, $file->loadFileData());
|
||||||
|
|
||||||
|
// The actual raw data in the storage engine should be encoded.
|
||||||
|
$raw_data = $engine->readFile($file->getStorageHandle());
|
||||||
|
$this->assertEqual($expect, $raw_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testAES256Storage() {
|
||||||
|
$engine = new PhabricatorTestStorageEngine();
|
||||||
|
|
||||||
|
$key_name = 'test.abcd';
|
||||||
|
$key_text = 'abcdefghijklmnopABCDEFGHIJKLMNOP';
|
||||||
|
|
||||||
|
PhabricatorKeyring::addKey(
|
||||||
|
array(
|
||||||
|
'name' => $key_name,
|
||||||
|
'type' => 'aes-256-cbc',
|
||||||
|
'material.base64' => base64_encode($key_text),
|
||||||
|
));
|
||||||
|
|
||||||
|
$format = id(new PhabricatorFileAES256StorageFormat())
|
||||||
|
->selectMasterKey($key_name);
|
||||||
|
|
||||||
|
$data = 'The cow jumped over the full moon.';
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'name' => 'test.dat',
|
||||||
|
'storageEngines' => array(
|
||||||
|
$engine,
|
||||||
|
),
|
||||||
|
'format' => $format,
|
||||||
|
);
|
||||||
|
|
||||||
|
$file = PhabricatorFile::newFromFileData($data, $params);
|
||||||
|
|
||||||
|
// We should have a file stored as AES256.
|
||||||
|
$format_key = $format->getStorageFormatKey();
|
||||||
|
$this->assertEqual($format_key, $file->getStorageFormat());
|
||||||
|
$this->assertEqual($data, $file->loadFileData());
|
||||||
|
|
||||||
|
// The actual raw data in the storage engine should be encrypted. We
|
||||||
|
// can't really test this, but we can make sure it's not the same as the
|
||||||
|
// input data.
|
||||||
|
$raw_data = $engine->readFile($file->getStorageHandle());
|
||||||
|
$this->assertTrue($data !== $raw_data);
|
||||||
|
}
|
||||||
|
}
|
52
src/applications/files/keyring/PhabricatorKeyring.php
Normal file
52
src/applications/files/keyring/PhabricatorKeyring.php
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorKeyring extends Phobject {
|
||||||
|
|
||||||
|
private static $hasReadConfiguration;
|
||||||
|
private static $keyRing = array();
|
||||||
|
|
||||||
|
public static function addKey($spec) {
|
||||||
|
self::$keyRing[$spec['name']] = $spec;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getKey($name, $type) {
|
||||||
|
self::readConfiguration();
|
||||||
|
|
||||||
|
if (empty(self::$keyRing[$name])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'No key "%s" exists in keyring.',
|
||||||
|
$name));
|
||||||
|
}
|
||||||
|
|
||||||
|
$spec = self::$keyRing[$name];
|
||||||
|
|
||||||
|
$material = base64_decode($spec['material.base64'], true);
|
||||||
|
return new PhutilOpaqueEnvelope($material);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static function getDefaultKeyName($type) {
|
||||||
|
self::readConfiguration();
|
||||||
|
|
||||||
|
foreach (self::$keyRing as $name => $key) {
|
||||||
|
if (!empty($key['default'])) {
|
||||||
|
return $name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static function readConfiguration() {
|
||||||
|
if (self::$hasReadConfiguration) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
self::$hasReadConfiguration = true;
|
||||||
|
|
||||||
|
foreach (PhabricatorEnv::getEnvConfig('keyring') as $spec) {
|
||||||
|
self::addKey($spec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,111 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorKeyringConfigOptionType
|
||||||
|
extends PhabricatorConfigJSONOptionType {
|
||||||
|
|
||||||
|
public function validateOption(PhabricatorConfigOption $option, $value) {
|
||||||
|
if (!is_array($value)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring configuration is not valid: value must be a '.
|
||||||
|
'list of encryption keys.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($value as $index => $spec) {
|
||||||
|
if (!is_array($spec)) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring configuration is not valid: each entry in the list must '.
|
||||||
|
'be a dictionary describing an encryption key, but the value '.
|
||||||
|
'with index "%s" is not a dictionary.',
|
||||||
|
$index));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$map = array();
|
||||||
|
$defaults = array();
|
||||||
|
foreach ($value as $index => $spec) {
|
||||||
|
try {
|
||||||
|
PhutilTypeSpec::checkMap(
|
||||||
|
$spec,
|
||||||
|
array(
|
||||||
|
'name' => 'string',
|
||||||
|
'type' => 'string',
|
||||||
|
'material.base64' => 'string',
|
||||||
|
'default' => 'optional bool',
|
||||||
|
));
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring configuration has an invalid key specification (at '.
|
||||||
|
'index "%s"): %s.',
|
||||||
|
$index,
|
||||||
|
$ex->getMessage()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$name = $spec['name'];
|
||||||
|
if (isset($map[$name])) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring configuration is invalid: it describes multiple keys '.
|
||||||
|
'with the same name ("%s"). Each key must have a unique name.',
|
||||||
|
$name));
|
||||||
|
}
|
||||||
|
$map[$name] = true;
|
||||||
|
|
||||||
|
if (idx($spec, 'default')) {
|
||||||
|
$defaults[] = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
$type = $spec['type'];
|
||||||
|
switch ($type) {
|
||||||
|
case 'aes-256-cbc':
|
||||||
|
if (!function_exists('openssl_encrypt')) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring is configured with a "%s" key, but the PHP OpenSSL '.
|
||||||
|
'extension is not installed. Install the OpenSSL extension '.
|
||||||
|
'to enable encryption.',
|
||||||
|
$type));
|
||||||
|
}
|
||||||
|
|
||||||
|
$material = $spec['material.base64'];
|
||||||
|
$material = base64_decode($material, true);
|
||||||
|
if ($material === false) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring specifies an invalid key ("%s"): key material '.
|
||||||
|
'should be base64 encoded.',
|
||||||
|
$name));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (strlen($material) != 32) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring specifies an invalid key ("%s"): key material '.
|
||||||
|
'should be 32 bytes (256 bits) but has length %s.',
|
||||||
|
$name,
|
||||||
|
new PhutilNumber(strlen($material))));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring configuration is invalid: it describes a key with '.
|
||||||
|
'type "%s", but this type is unknown.',
|
||||||
|
$type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count($defaults) > 1) {
|
||||||
|
throw new Exception(
|
||||||
|
pht(
|
||||||
|
'Keyring configuration is invalid: it describes multiple default '.
|
||||||
|
'encryption keys. No more than one key may be the default key. '.
|
||||||
|
'Keys currently configured as defaults: %s.',
|
||||||
|
implode(', ', $defaults)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,132 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFilesManagementCycleWorkflow
|
||||||
|
extends PhabricatorFilesManagementWorkflow {
|
||||||
|
|
||||||
|
protected function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('cycle')
|
||||||
|
->setSynopsis(
|
||||||
|
pht('Cycle master key for encrypted files.'))
|
||||||
|
->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'key',
|
||||||
|
'param' => 'keyname',
|
||||||
|
'help' => pht('Select a specific storage key to cycle to.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'all',
|
||||||
|
'help' => pht('Change encoding for all files.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'names',
|
||||||
|
'wildcard' => true,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$iterator = $this->buildIterator($args);
|
||||||
|
if (!$iterator) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Either specify a list of files to cycle, or use --all to cycle '.
|
||||||
|
'all files.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$format_map = PhabricatorFileStorageFormat::getAllFormats();
|
||||||
|
$engines = PhabricatorFileStorageEngine::loadAllEngines();
|
||||||
|
|
||||||
|
$key_name = $args->getArg('key');
|
||||||
|
|
||||||
|
$failed = array();
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
$monogram = $file->getMonogram();
|
||||||
|
|
||||||
|
$engine_key = $file->getStorageEngine();
|
||||||
|
$engine = idx($engines, $engine_key);
|
||||||
|
|
||||||
|
if (!$engine) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Uses unknown storage engine "%s".',
|
||||||
|
$monogram,
|
||||||
|
$engine_key));
|
||||||
|
$failed[] = $file;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($engine->isChunkEngine()) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Stored as chunks, declining to cycle directly.',
|
||||||
|
$monogram));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$format_key = $file->getStorageFormat();
|
||||||
|
if (empty($format_map[$format_key])) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Uses unknown storage format "%s".',
|
||||||
|
$monogram,
|
||||||
|
$format_key));
|
||||||
|
$failed[] = $file;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$format = clone $format_map[$format_key];
|
||||||
|
$format->setFile($file);
|
||||||
|
|
||||||
|
if (!$format->canCycleMasterKey()) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Storage format ("%s") does not support key cycling.',
|
||||||
|
$monogram,
|
||||||
|
$format->getStorageFormatName()));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Cycling master key.',
|
||||||
|
$monogram));
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ($key_name) {
|
||||||
|
$format->selectMasterKey($key_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
$file->cycleMasterStorageKey($format);
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht('Done.'));
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%B\n",
|
||||||
|
pht('Failed! %s', (string)$ex));
|
||||||
|
$failed[] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($failed) {
|
||||||
|
$monograms = mpull($failed, 'getMonogram');
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht('Failures: %s.', implode(', ', $monograms)));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,151 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFilesManagementEncodeWorkflow
|
||||||
|
extends PhabricatorFilesManagementWorkflow {
|
||||||
|
|
||||||
|
protected function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('encode')
|
||||||
|
->setSynopsis(
|
||||||
|
pht('Change the storage encoding of files.'))
|
||||||
|
->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'as',
|
||||||
|
'param' => 'format',
|
||||||
|
'help' => pht('Select the storage format to use.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'key',
|
||||||
|
'param' => 'keyname',
|
||||||
|
'help' => pht('Select a specific storage key.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'all',
|
||||||
|
'help' => pht('Change encoding for all files.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'force',
|
||||||
|
'help' => pht(
|
||||||
|
'Re-encode files which are already stored in the target '.
|
||||||
|
'encoding.'),
|
||||||
|
),
|
||||||
|
array(
|
||||||
|
'name' => 'names',
|
||||||
|
'wildcard' => true,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$iterator = $this->buildIterator($args);
|
||||||
|
if (!$iterator) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Either specify a list of files to encode, or use --all to '.
|
||||||
|
'encode all files.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$force = (bool)$args->getArg('force');
|
||||||
|
|
||||||
|
$format_list = PhabricatorFileStorageFormat::getAllFormats();
|
||||||
|
$format_list = array_keys($format_list);
|
||||||
|
$format_list = implode(', ', $format_list);
|
||||||
|
|
||||||
|
$format_key = $args->getArg('as');
|
||||||
|
if (!strlen($format_key)) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Use --as <format> to select a target encoding format. Available '.
|
||||||
|
'formats are: %s.',
|
||||||
|
$format_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
$format = PhabricatorFileStorageFormat::getFormat($format_key);
|
||||||
|
if (!$format) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Storage format "%s" is not valid. Available formats are: %s.',
|
||||||
|
$format_key,
|
||||||
|
$format_list));
|
||||||
|
}
|
||||||
|
|
||||||
|
$key_name = $args->getArg('key');
|
||||||
|
if (strlen($key_name)) {
|
||||||
|
$format->selectMasterKey($key_name);
|
||||||
|
}
|
||||||
|
|
||||||
|
$engines = PhabricatorFileStorageEngine::loadAllEngines();
|
||||||
|
|
||||||
|
$failed = array();
|
||||||
|
foreach ($iterator as $file) {
|
||||||
|
$monogram = $file->getMonogram();
|
||||||
|
|
||||||
|
$engine_key = $file->getStorageEngine();
|
||||||
|
$engine = idx($engines, $engine_key);
|
||||||
|
|
||||||
|
if (!$engine) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Uses unknown storage engine "%s".',
|
||||||
|
$monogram,
|
||||||
|
$engine_key));
|
||||||
|
$failed[] = $file;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($engine->isChunkEngine()) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Stored as chunks, no data to encode directly.',
|
||||||
|
$monogram));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (($file->getStorageFormat() == $format_key) && !$force) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Already encoded in target format.',
|
||||||
|
$monogram));
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'%s: Changing encoding from "%s" to "%s".',
|
||||||
|
$monogram,
|
||||||
|
$file->getStorageFormat(),
|
||||||
|
$format_key));
|
||||||
|
|
||||||
|
try {
|
||||||
|
$file->migrateToStorageFormat($format);
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht('Done.'));
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%B\n",
|
||||||
|
pht('Failed! %s', (string)$ex));
|
||||||
|
$failed[] = $file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($failed) {
|
||||||
|
$monograms = mpull($failed, 'getMonogram');
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht('Failures: %s.', implode(', ', $monograms)));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorFilesManagementGenerateKeyWorkflow
|
||||||
|
extends PhabricatorFilesManagementWorkflow {
|
||||||
|
|
||||||
|
protected function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('generate-key')
|
||||||
|
->setSynopsis(
|
||||||
|
pht('Generate an encryption key.'))
|
||||||
|
->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'type',
|
||||||
|
'param' => 'keytype',
|
||||||
|
'help' => pht('Select the type of key to generate.'),
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$type = $args->getArg('type');
|
||||||
|
if (!strlen($type)) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specify the type of key to generate with --type.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
$format = PhabricatorFileStorageFormat::getFormat($type);
|
||||||
|
if (!$format) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'No key type "%s" exists.',
|
||||||
|
$type));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$format->canGenerateNewKeyMaterial()) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Storage format "%s" can not generate keys.',
|
||||||
|
$format->getStorageFormatName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$material = $format->generateNewKeyMaterial();
|
||||||
|
|
||||||
|
$structure = array(
|
||||||
|
'name' => 'generated-key-'.Filesystem::readRandomCharacters(12),
|
||||||
|
'type' => $type,
|
||||||
|
'material.base64' => $material,
|
||||||
|
);
|
||||||
|
|
||||||
|
$json = id(new PhutilJSON())->encodeFormatted($structure);
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s: %s\n\n%B\n",
|
||||||
|
pht('Key Material'),
|
||||||
|
$format->getStorageFormatName(),
|
||||||
|
$json);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -26,14 +26,13 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
PhabricatorPolicyInterface,
|
PhabricatorPolicyInterface,
|
||||||
PhabricatorDestructibleInterface {
|
PhabricatorDestructibleInterface {
|
||||||
|
|
||||||
const STORAGE_FORMAT_RAW = 'raw';
|
|
||||||
|
|
||||||
const METADATA_IMAGE_WIDTH = 'width';
|
const METADATA_IMAGE_WIDTH = 'width';
|
||||||
const METADATA_IMAGE_HEIGHT = 'height';
|
const METADATA_IMAGE_HEIGHT = 'height';
|
||||||
const METADATA_CAN_CDN = 'canCDN';
|
const METADATA_CAN_CDN = 'canCDN';
|
||||||
const METADATA_BUILTIN = 'builtin';
|
const METADATA_BUILTIN = 'builtin';
|
||||||
const METADATA_PARTIAL = 'partial';
|
const METADATA_PARTIAL = 'partial';
|
||||||
const METADATA_PROFILE = 'profile';
|
const METADATA_PROFILE = 'profile';
|
||||||
|
const METADATA_STORAGE = 'storage';
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
protected $mimeType;
|
protected $mimeType;
|
||||||
|
@ -233,10 +232,10 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
$hash);
|
$hash);
|
||||||
|
|
||||||
if ($file) {
|
if ($file) {
|
||||||
// copy storageEngine, storageHandle, storageFormat
|
|
||||||
$copy_of_storage_engine = $file->getStorageEngine();
|
$copy_of_storage_engine = $file->getStorageEngine();
|
||||||
$copy_of_storage_handle = $file->getStorageHandle();
|
$copy_of_storage_handle = $file->getStorageHandle();
|
||||||
$copy_of_storage_format = $file->getStorageFormat();
|
$copy_of_storage_format = $file->getStorageFormat();
|
||||||
|
$copy_of_storage_properties = $file->getStorageProperties();
|
||||||
$copy_of_byte_size = $file->getByteSize();
|
$copy_of_byte_size = $file->getByteSize();
|
||||||
$copy_of_mime_type = $file->getMimeType();
|
$copy_of_mime_type = $file->getMimeType();
|
||||||
|
|
||||||
|
@ -248,6 +247,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
$new_file->setStorageEngine($copy_of_storage_engine);
|
$new_file->setStorageEngine($copy_of_storage_engine);
|
||||||
$new_file->setStorageHandle($copy_of_storage_handle);
|
$new_file->setStorageHandle($copy_of_storage_handle);
|
||||||
$new_file->setStorageFormat($copy_of_storage_format);
|
$new_file->setStorageFormat($copy_of_storage_format);
|
||||||
|
$new_file->setStorageProperties($copy_of_storage_properties);
|
||||||
$new_file->setMimeType($copy_of_mime_type);
|
$new_file->setMimeType($copy_of_mime_type);
|
||||||
$new_file->copyDimensions($file);
|
$new_file->copyDimensions($file);
|
||||||
|
|
||||||
|
@ -290,7 +290,11 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
|
|
||||||
$file->setStorageEngine($engine->getEngineIdentifier());
|
$file->setStorageEngine($engine->getEngineIdentifier());
|
||||||
$file->setStorageHandle(PhabricatorFileChunk::newChunkHandle());
|
$file->setStorageHandle(PhabricatorFileChunk::newChunkHandle());
|
||||||
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
|
|
||||||
|
// Chunked files are always stored raw because they do not actually store
|
||||||
|
// data. The chunks do, and can be individually formatted.
|
||||||
|
$file->setStorageFormat(PhabricatorFileRawStorageFormat::FORMATKEY);
|
||||||
|
|
||||||
$file->setIsPartial(1);
|
$file->setIsPartial(1);
|
||||||
|
|
||||||
$file->readPropertiesFromParameters($params);
|
$file->readPropertiesFromParameters($params);
|
||||||
|
@ -322,6 +326,29 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
|
|
||||||
$file = self::initializeNewFile();
|
$file = self::initializeNewFile();
|
||||||
|
|
||||||
|
$aes_type = PhabricatorFileAES256StorageFormat::FORMATKEY;
|
||||||
|
$has_aes = PhabricatorKeyring::getDefaultKeyName($aes_type);
|
||||||
|
if ($has_aes !== null) {
|
||||||
|
$default_key = PhabricatorFileAES256StorageFormat::FORMATKEY;
|
||||||
|
} else {
|
||||||
|
$default_key = PhabricatorFileRawStorageFormat::FORMATKEY;
|
||||||
|
}
|
||||||
|
$key = idx($params, 'format', $default_key);
|
||||||
|
|
||||||
|
// Callers can pass in an object explicitly instead of a key. This is
|
||||||
|
// primarily useful for unit tests.
|
||||||
|
if ($key instanceof PhabricatorFileStorageFormat) {
|
||||||
|
$format = clone $key;
|
||||||
|
} else {
|
||||||
|
$format = clone PhabricatorFileStorageFormat::requireFormat($key);
|
||||||
|
}
|
||||||
|
|
||||||
|
$format->setFile($file);
|
||||||
|
|
||||||
|
$properties = $format->newStorageProperties();
|
||||||
|
$file->setStorageFormat($format->getStorageFormatKey());
|
||||||
|
$file->setStorageProperties($properties);
|
||||||
|
|
||||||
$data_handle = null;
|
$data_handle = null;
|
||||||
$engine_identifier = null;
|
$engine_identifier = null;
|
||||||
$exceptions = array();
|
$exceptions = array();
|
||||||
|
@ -361,10 +388,6 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
$file->setStorageEngine($engine_identifier);
|
$file->setStorageEngine($engine_identifier);
|
||||||
$file->setStorageHandle($data_handle);
|
$file->setStorageHandle($data_handle);
|
||||||
|
|
||||||
// TODO: This is probably YAGNI, but allows for us to do encryption or
|
|
||||||
// compression later if we want.
|
|
||||||
$file->setStorageFormat(self::STORAGE_FORMAT_RAW);
|
|
||||||
|
|
||||||
$file->readPropertiesFromParameters($params);
|
$file->readPropertiesFromParameters($params);
|
||||||
|
|
||||||
if (!$file->getMimeType()) {
|
if (!$file->getMimeType()) {
|
||||||
|
@ -427,6 +450,53 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function migrateToStorageFormat(PhabricatorFileStorageFormat $format) {
|
||||||
|
if (!$this->getID() || !$this->getStorageHandle()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht("You can not migrate a file which hasn't yet been saved."));
|
||||||
|
}
|
||||||
|
|
||||||
|
$data = $this->loadFileData();
|
||||||
|
$params = array(
|
||||||
|
'name' => $this->getName(),
|
||||||
|
);
|
||||||
|
|
||||||
|
$engine = $this->instantiateStorageEngine();
|
||||||
|
$old_handle = $this->getStorageHandle();
|
||||||
|
|
||||||
|
$properties = $format->newStorageProperties();
|
||||||
|
$this->setStorageFormat($format->getStorageFormatKey());
|
||||||
|
$this->setStorageProperties($properties);
|
||||||
|
|
||||||
|
list($identifier, $new_handle) = $this->writeToEngine(
|
||||||
|
$engine,
|
||||||
|
$data,
|
||||||
|
$params);
|
||||||
|
|
||||||
|
$this->setStorageHandle($new_handle);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
$this->deleteFileDataIfUnused(
|
||||||
|
$engine,
|
||||||
|
$identifier,
|
||||||
|
$old_handle);
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function cycleMasterStorageKey(PhabricatorFileStorageFormat $format) {
|
||||||
|
if (!$this->getID() || !$this->getStorageHandle()) {
|
||||||
|
throw new Exception(
|
||||||
|
pht("You can not cycle keys for a file which hasn't yet been saved."));
|
||||||
|
}
|
||||||
|
|
||||||
|
$properties = $format->cycleStorageProperties();
|
||||||
|
$this->setStorageProperties($properties);
|
||||||
|
$this->save();
|
||||||
|
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
private function writeToEngine(
|
private function writeToEngine(
|
||||||
PhabricatorFileStorageEngine $engine,
|
PhabricatorFileStorageEngine $engine,
|
||||||
$data,
|
$data,
|
||||||
|
@ -434,7 +504,15 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
|
|
||||||
$engine_class = get_class($engine);
|
$engine_class = get_class($engine);
|
||||||
|
|
||||||
$data_handle = $engine->writeFile($data, $params);
|
$key = $this->getStorageFormat();
|
||||||
|
$format = id(clone PhabricatorFileStorageFormat::requireFormat($key))
|
||||||
|
->setFile($this);
|
||||||
|
|
||||||
|
$data_iterator = array($data);
|
||||||
|
$formatted_iterator = $format->newWriteIterator($data_iterator);
|
||||||
|
$formatted_data = $this->loadDataFromIterator($formatted_iterator);
|
||||||
|
|
||||||
|
$data_handle = $engine->writeFile($formatted_data, $params);
|
||||||
|
|
||||||
if (!$data_handle || strlen($data_handle) > 255) {
|
if (!$data_handle || strlen($data_handle) > 255) {
|
||||||
// This indicates an improperly implemented storage engine.
|
// This indicates an improperly implemented storage engine.
|
||||||
|
@ -663,19 +741,8 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function loadFileData() {
|
public function loadFileData() {
|
||||||
|
$iterator = $this->getFileDataIterator();
|
||||||
$engine = $this->instantiateStorageEngine();
|
return $this->loadDataFromIterator($iterator);
|
||||||
$data = $engine->readFile($this->getStorageHandle());
|
|
||||||
|
|
||||||
switch ($this->getStorageFormat()) {
|
|
||||||
case self::STORAGE_FORMAT_RAW:
|
|
||||||
$data = $data;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new Exception(pht('Unknown storage format.'));
|
|
||||||
}
|
|
||||||
|
|
||||||
return $data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -688,7 +755,14 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
*/
|
*/
|
||||||
public function getFileDataIterator($begin = null, $end = null) {
|
public function getFileDataIterator($begin = null, $end = null) {
|
||||||
$engine = $this->instantiateStorageEngine();
|
$engine = $this->instantiateStorageEngine();
|
||||||
return $engine->getFileDataIterator($this, $begin, $end);
|
$raw_iterator = $engine->getRawFileDataIterator($this, $begin, $end);
|
||||||
|
|
||||||
|
$key = $this->getStorageFormat();
|
||||||
|
|
||||||
|
$format = id(clone PhabricatorFileStorageFormat::requireFormat($key))
|
||||||
|
->setFile($this);
|
||||||
|
|
||||||
|
return $format->newReadIterator($raw_iterator);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -917,6 +991,30 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
||||||
return Filesystem::readRandomCharacters(20);
|
return Filesystem::readRandomCharacters(20);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function setStorageProperties(array $properties) {
|
||||||
|
$this->metadata[self::METADATA_STORAGE] = $properties;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStorageProperties() {
|
||||||
|
return idx($this->metadata, self::METADATA_STORAGE, array());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getStorageProperty($key, $default = null) {
|
||||||
|
$properties = $this->getStorageProperties();
|
||||||
|
return idx($properties, $key, $default);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function loadDataFromIterator($iterator) {
|
||||||
|
$result = '';
|
||||||
|
|
||||||
|
foreach ($iterator as $chunk) {
|
||||||
|
$result .= $chunk;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
public function updateDimensions($save = true) {
|
public function updateDimensions($save = true) {
|
||||||
if (!$this->isViewableImage()) {
|
if (!$this->isViewableImage()) {
|
||||||
throw new Exception(pht('This file is not a viewable image.'));
|
throw new Exception(pht('This file is not a viewable image.'));
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
private $build;
|
private $build;
|
||||||
private $viewer;
|
private $viewer;
|
||||||
private $newBuildTargets = array();
|
private $newBuildTargets = array();
|
||||||
|
private $artifactReleaseQueue = array();
|
||||||
private $forceBuildableUpdate;
|
private $forceBuildableUpdate;
|
||||||
|
|
||||||
public function setForceBuildableUpdate($force_buildable_update) {
|
public function setForceBuildableUpdate($force_buildable_update) {
|
||||||
|
@ -94,6 +95,8 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
$this->updateBuildable($build->getBuildable());
|
$this->updateBuildable($build->getBuildable());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->releaseQueuedArtifacts();
|
||||||
|
|
||||||
// If we are no longer building for any reason, release all artifacts.
|
// If we are no longer building for any reason, release all artifacts.
|
||||||
if (!$build->isBuilding()) {
|
if (!$build->isBuilding()) {
|
||||||
$this->releaseAllArtifacts($build);
|
$this->releaseAllArtifacts($build);
|
||||||
|
@ -149,20 +152,21 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateBuildSteps(HarbormasterBuild $build) {
|
private function updateBuildSteps(HarbormasterBuild $build) {
|
||||||
$targets = id(new HarbormasterBuildTargetQuery())
|
$all_targets = id(new HarbormasterBuildTargetQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withBuildPHIDs(array($build->getPHID()))
|
->withBuildPHIDs(array($build->getPHID()))
|
||||||
->withBuildGenerations(array($build->getBuildGeneration()))
|
->withBuildGenerations(array($build->getBuildGeneration()))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$this->updateWaitingTargets($targets);
|
$this->updateWaitingTargets($all_targets);
|
||||||
|
|
||||||
$targets = mgroup($targets, 'getBuildStepPHID');
|
$targets = mgroup($all_targets, 'getBuildStepPHID');
|
||||||
|
|
||||||
$steps = id(new HarbormasterBuildStepQuery())
|
$steps = id(new HarbormasterBuildStepQuery())
|
||||||
->setViewer($this->getViewer())
|
->setViewer($this->getViewer())
|
||||||
->withBuildPlanPHIDs(array($build->getBuildPlan()->getPHID()))
|
->withBuildPlanPHIDs(array($build->getBuildPlan()->getPHID()))
|
||||||
->execute();
|
->execute();
|
||||||
|
$steps = mpull($steps, null, 'getPHID');
|
||||||
|
|
||||||
// Identify steps which are in various states.
|
// Identify steps which are in various states.
|
||||||
|
|
||||||
|
@ -252,6 +256,12 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Release any artifacts which are not inputs to any remaining build
|
||||||
|
// step. We're done with these, so something else is free to use them.
|
||||||
|
$ongoing_phids = array_keys($queued + $waiting + $underway);
|
||||||
|
$ongoing_steps = array_select_keys($steps, $ongoing_phids);
|
||||||
|
$this->releaseUnusedArtifacts($all_targets, $ongoing_steps);
|
||||||
|
|
||||||
// Identify all the steps which are ready to run (because all their
|
// Identify all the steps which are ready to run (because all their
|
||||||
// dependencies are complete).
|
// dependencies are complete).
|
||||||
|
|
||||||
|
@ -294,6 +304,59 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Release any artifacts which aren't used by any running or waiting steps.
|
||||||
|
*
|
||||||
|
* This releases artifacts as soon as they're no longer used. This can be
|
||||||
|
* particularly relevant when a build uses multiple hosts since it returns
|
||||||
|
* hosts to the pool more quickly.
|
||||||
|
*
|
||||||
|
* @param list<HarbormasterBuildTarget> Targets in the build.
|
||||||
|
* @param list<HarbormasterBuildStep> List of running and waiting steps.
|
||||||
|
* @return void
|
||||||
|
*/
|
||||||
|
private function releaseUnusedArtifacts(array $targets, array $steps) {
|
||||||
|
assert_instances_of($targets, 'HarbormasterBuildTarget');
|
||||||
|
assert_instances_of($steps, 'HarbormasterBuildStep');
|
||||||
|
|
||||||
|
if (!$targets || !$steps) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$target_phids = mpull($targets, 'getPHID');
|
||||||
|
|
||||||
|
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withBuildTargetPHIDs($target_phids)
|
||||||
|
->withIsReleased(false)
|
||||||
|
->execute();
|
||||||
|
if (!$artifacts) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Collect all the artifacts that remaining build steps accept as inputs.
|
||||||
|
$must_keep = array();
|
||||||
|
foreach ($steps as $step) {
|
||||||
|
$inputs = $step->getStepImplementation()->getArtifactInputs();
|
||||||
|
foreach ($inputs as $input) {
|
||||||
|
$artifact_key = $input['key'];
|
||||||
|
$must_keep[$artifact_key] = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Queue unreleased artifacts which no remaining step uses for immediate
|
||||||
|
// release.
|
||||||
|
foreach ($artifacts as $artifact) {
|
||||||
|
$key = $artifact->getArtifactKey();
|
||||||
|
if (isset($must_keep[$key])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->artifactReleaseQueue[] = $artifact;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Process messages which were sent to these targets, kicking applicable
|
* Process messages which were sent to these targets, kicking applicable
|
||||||
* targets out of "Waiting" and into either "Passed" or "Failed".
|
* targets out of "Waiting" and into either "Passed" or "Failed".
|
||||||
|
@ -488,12 +551,18 @@ final class HarbormasterBuildEngine extends Phobject {
|
||||||
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
$artifacts = id(new HarbormasterBuildArtifactQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
->withBuildTargetPHIDs($target_phids)
|
->withBuildTargetPHIDs($target_phids)
|
||||||
|
->withIsReleased(false)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
foreach ($artifacts as $artifact) {
|
foreach ($artifacts as $artifact) {
|
||||||
$artifact->releaseArtifact();
|
$artifact->releaseArtifact();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function releaseQueuedArtifacts() {
|
||||||
|
foreach ($this->artifactReleaseQueue as $key => $artifact) {
|
||||||
|
$artifact->releaseArtifact();
|
||||||
|
unset($this->artifactReleaseQueue[$key]);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class HarbormasterBuildArtifactQuery
|
||||||
private $artifactIndexes;
|
private $artifactIndexes;
|
||||||
private $keyBuildPHID;
|
private $keyBuildPHID;
|
||||||
private $keyBuildGeneration;
|
private $keyBuildGeneration;
|
||||||
|
private $isReleased;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -30,6 +31,11 @@ final class HarbormasterBuildArtifactQuery
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function withIsReleased($released) {
|
||||||
|
$this->isReleased = $released;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new HarbormasterBuildArtifact();
|
return new HarbormasterBuildArtifact();
|
||||||
}
|
}
|
||||||
|
@ -94,6 +100,13 @@ final class HarbormasterBuildArtifactQuery
|
||||||
$this->artifactIndexes);
|
$this->artifactIndexes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->isReleased !== null) {
|
||||||
|
$where[] = qsprintf(
|
||||||
|
$conn,
|
||||||
|
'isReleased = %d',
|
||||||
|
(int)$this->isReleased);
|
||||||
|
}
|
||||||
|
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -103,11 +103,6 @@ abstract class HarbormasterBuildStepImplementation extends Phobject {
|
||||||
/**
|
/**
|
||||||
* Return the name of artifacts produced by this command.
|
* Return the name of artifacts produced by this command.
|
||||||
*
|
*
|
||||||
* Something like:
|
|
||||||
*
|
|
||||||
* return array(
|
|
||||||
* 'some_name_input_by_user' => 'host');
|
|
||||||
*
|
|
||||||
* Future steps will calculate all available artifact mappings
|
* Future steps will calculate all available artifact mappings
|
||||||
* before them and filter on the type.
|
* before them and filter on the type.
|
||||||
*
|
*
|
||||||
|
|
|
@ -8,6 +8,7 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
||||||
protected $artifactIndex;
|
protected $artifactIndex;
|
||||||
protected $artifactKey;
|
protected $artifactKey;
|
||||||
protected $artifactData = array();
|
protected $artifactData = array();
|
||||||
|
protected $isReleased = 0;
|
||||||
|
|
||||||
private $buildTarget = self::ATTACHABLE;
|
private $buildTarget = self::ATTACHABLE;
|
||||||
private $artifactImplementation;
|
private $artifactImplementation;
|
||||||
|
@ -29,6 +30,7 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
||||||
'artifactType' => 'text32',
|
'artifactType' => 'text32',
|
||||||
'artifactIndex' => 'bytes12',
|
'artifactIndex' => 'bytes12',
|
||||||
'artifactKey' => 'text255',
|
'artifactKey' => 'text255',
|
||||||
|
'isReleased' => 'bool',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_artifact' => array(
|
'key_artifact' => array(
|
||||||
|
@ -83,13 +85,18 @@ final class HarbormasterBuildArtifact extends HarbormasterDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
public function releaseArtifact() {
|
public function releaseArtifact() {
|
||||||
$impl = $this->getArtifactImplementation();
|
if ($this->getIsReleased()) {
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
$impl = $this->getArtifactImplementation();
|
||||||
if ($impl) {
|
if ($impl) {
|
||||||
$impl->releaseArtifact(PhabricatorUser::getOmnipotentUser());
|
$impl->releaseArtifact(PhabricatorUser::getOmnipotentUser());
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
return $this
|
||||||
|
->setIsReleased(1)
|
||||||
|
->save();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getArtifactImplementation() {
|
public function getArtifactImplementation() {
|
||||||
|
|
|
@ -54,16 +54,18 @@ final class ManiphestTransaction
|
||||||
return parent::shouldGenerateOldValue();
|
return parent::shouldGenerateOldValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRemarkupBlocks() {
|
protected function newRemarkupChanges() {
|
||||||
$blocks = parent::getRemarkupBlocks();
|
$changes = array();
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
switch ($this->getTransactionType()) {
|
||||||
case self::TYPE_DESCRIPTION:
|
case self::TYPE_DESCRIPTION:
|
||||||
$blocks[] = $this->getNewValue();
|
$changes[] = $this->newRemarkupChange()
|
||||||
|
->setOldValue($this->getOldValue())
|
||||||
|
->setNewValue($this->getNewValue());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return $blocks;
|
return $changes;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequiredHandlePHIDs() {
|
public function getRequiredHandlePHIDs() {
|
||||||
|
|
|
@ -877,6 +877,7 @@ final class PhabricatorMetaMTAMail
|
||||||
$all_prefs = id(new PhabricatorUserPreferencesQuery())
|
$all_prefs = id(new PhabricatorUserPreferencesQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
->withUserPHIDs($actor_phids)
|
->withUserPHIDs($actor_phids)
|
||||||
|
->needSyntheticPreferences(true)
|
||||||
->execute();
|
->execute();
|
||||||
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
|
$all_prefs = mpull($all_prefs, null, 'getUserPHID');
|
||||||
|
|
||||||
|
@ -1105,53 +1106,33 @@ final class PhabricatorMetaMTAMail
|
||||||
|
|
||||||
|
|
||||||
private function loadPreferences($target_phid) {
|
private function loadPreferences($target_phid) {
|
||||||
if (!self::shouldMultiplexAllMail()) {
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
$target_phid = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ($target_phid) {
|
if (self::shouldMultiplexAllMail()) {
|
||||||
$preferences = id(new PhabricatorUserPreferencesQuery())
|
$preferences = id(new PhabricatorUserPreferencesQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer($viewer)
|
||||||
->withUserPHIDs(array($target_phid))
|
->withUserPHIDs(array($target_phid))
|
||||||
|
->needSyntheticPreferences(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
} else {
|
if ($preferences) {
|
||||||
$preferences = null;
|
return $preferences;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Here, we would load global preferences once they exist.
|
return PhabricatorUserPreferences::loadGlobalPreferences($viewer);
|
||||||
|
|
||||||
if (!$preferences) {
|
|
||||||
// If we haven't found suitable preferences yet, return an empty object
|
|
||||||
// which implicitly has all the default values.
|
|
||||||
$preferences = id(new PhabricatorUserPreferences())
|
|
||||||
->attachUser(new PhabricatorUser());
|
|
||||||
}
|
|
||||||
|
|
||||||
return $preferences;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function shouldAddRePrefix(PhabricatorUserPreferences $preferences) {
|
private function shouldAddRePrefix(PhabricatorUserPreferences $preferences) {
|
||||||
$default_value = PhabricatorEnv::getEnvConfig('metamta.re-prefix');
|
$value = $preferences->getSettingValue(
|
||||||
|
|
||||||
$value = $preferences->getPreference(
|
|
||||||
PhabricatorEmailRePrefixSetting::SETTINGKEY);
|
PhabricatorEmailRePrefixSetting::SETTINGKEY);
|
||||||
if ($value === null) {
|
|
||||||
return $default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($value == PhabricatorEmailRePrefixSetting::VALUE_RE_PREFIX);
|
return ($value == PhabricatorEmailRePrefixSetting::VALUE_RE_PREFIX);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function shouldVarySubject(PhabricatorUserPreferences $preferences) {
|
private function shouldVarySubject(PhabricatorUserPreferences $preferences) {
|
||||||
$default_value = PhabricatorEnv::getEnvConfig('metamta.vary-subjects');
|
$value = $preferences->getSettingValue(
|
||||||
|
|
||||||
$value = $preferences->getPreference(
|
|
||||||
PhabricatorEmailVarySubjectsSetting::SETTINGKEY);
|
PhabricatorEmailVarySubjectsSetting::SETTINGKEY);
|
||||||
|
|
||||||
if ($value === null) {
|
|
||||||
return $default_value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return ($value == PhabricatorEmailVarySubjectsSetting::VALUE_VARY_SUBJECTS);
|
return ($value == PhabricatorEmailVarySubjectsSetting::VALUE_VARY_SUBJECTS);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -47,15 +47,15 @@ final class PasteCreateConduitAPIMethod extends PasteConduitAPIMethod {
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT)
|
->setTransactionType(PhabricatorPasteContentTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($content);
|
->setNewValue($content);
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE)
|
->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($title);
|
->setNewValue($title);
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE)
|
->setTransactionType(PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($language);
|
->setNewValue($language);
|
||||||
|
|
||||||
$editor = id(new PhabricatorPasteEditor())
|
$editor = id(new PhabricatorPasteEditor())
|
||||||
|
|
|
@ -20,7 +20,7 @@ final class PhabricatorPasteArchiveController
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
}
|
}
|
||||||
|
|
||||||
$view_uri = '/P'.$paste->getID();
|
$view_uri = $paste->getURI();
|
||||||
|
|
||||||
if ($request->isFormPost()) {
|
if ($request->isFormPost()) {
|
||||||
if ($paste->isArchived()) {
|
if ($paste->isArchived()) {
|
||||||
|
@ -32,7 +32,7 @@ final class PhabricatorPasteArchiveController
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_STATUS)
|
->setTransactionType(PhabricatorPasteStatusTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($new_status);
|
->setNewValue($new_status);
|
||||||
|
|
||||||
id(new PhabricatorPasteEditor())
|
id(new PhabricatorPasteEditor())
|
||||||
|
|
|
@ -71,7 +71,7 @@ final class PhabricatorPasteEditEngine
|
||||||
id(new PhabricatorTextEditField())
|
id(new PhabricatorTextEditField())
|
||||||
->setKey('title')
|
->setKey('title')
|
||||||
->setLabel(pht('Title'))
|
->setLabel(pht('Title'))
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE)
|
->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE)
|
||||||
->setDescription(pht('The title of the paste.'))
|
->setDescription(pht('The title of the paste.'))
|
||||||
->setConduitDescription(pht('Retitle the paste.'))
|
->setConduitDescription(pht('Retitle the paste.'))
|
||||||
->setConduitTypeDescription(pht('New paste title.'))
|
->setConduitTypeDescription(pht('New paste title.'))
|
||||||
|
@ -79,7 +79,8 @@ final class PhabricatorPasteEditEngine
|
||||||
id(new PhabricatorSelectEditField())
|
id(new PhabricatorSelectEditField())
|
||||||
->setKey('language')
|
->setKey('language')
|
||||||
->setLabel(pht('Language'))
|
->setLabel(pht('Language'))
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE)
|
->setTransactionType(
|
||||||
|
PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE)
|
||||||
->setAliases(array('lang'))
|
->setAliases(array('lang'))
|
||||||
->setIsCopyable(true)
|
->setIsCopyable(true)
|
||||||
->setOptions($langs)
|
->setOptions($langs)
|
||||||
|
@ -94,7 +95,8 @@ final class PhabricatorPasteEditEngine
|
||||||
id(new PhabricatorTextAreaEditField())
|
id(new PhabricatorTextAreaEditField())
|
||||||
->setKey('text')
|
->setKey('text')
|
||||||
->setLabel(pht('Text'))
|
->setLabel(pht('Text'))
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT)
|
->setTransactionType(
|
||||||
|
PhabricatorPasteContentTransaction::TRANSACTIONTYPE)
|
||||||
->setMonospaced(true)
|
->setMonospaced(true)
|
||||||
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_TALL)
|
||||||
->setDescription(pht('The main body text of the paste.'))
|
->setDescription(pht('The main body text of the paste.'))
|
||||||
|
@ -104,7 +106,8 @@ final class PhabricatorPasteEditEngine
|
||||||
id(new PhabricatorSelectEditField())
|
id(new PhabricatorSelectEditField())
|
||||||
->setKey('status')
|
->setKey('status')
|
||||||
->setLabel(pht('Status'))
|
->setLabel(pht('Status'))
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_STATUS)
|
->setTransactionType(
|
||||||
|
PhabricatorPasteStatusTransaction::TRANSACTIONTYPE)
|
||||||
->setIsConduitOnly(true)
|
->setIsConduitOnly(true)
|
||||||
->setOptions(PhabricatorPaste::getStatusNameMap())
|
->setOptions(PhabricatorPaste::getStatusNameMap())
|
||||||
->setDescription(pht('Active or archived status.'))
|
->setDescription(pht('Active or archived status.'))
|
||||||
|
|
|
@ -3,8 +3,6 @@
|
||||||
final class PhabricatorPasteEditor
|
final class PhabricatorPasteEditor
|
||||||
extends PhabricatorApplicationTransactionEditor {
|
extends PhabricatorApplicationTransactionEditor {
|
||||||
|
|
||||||
private $fileName;
|
|
||||||
|
|
||||||
public function getEditorApplicationClass() {
|
public function getEditorApplicationClass() {
|
||||||
return 'PhabricatorPasteApplication';
|
return 'PhabricatorPasteApplication';
|
||||||
}
|
}
|
||||||
|
@ -13,29 +11,17 @@ final class PhabricatorPasteEditor
|
||||||
return pht('Pastes');
|
return pht('Pastes');
|
||||||
}
|
}
|
||||||
|
|
||||||
public static function initializeFileForPaste(
|
public function getCreateObjectTitle($author, $object) {
|
||||||
PhabricatorUser $actor,
|
return pht('%s created this paste.', $author);
|
||||||
$name,
|
}
|
||||||
$data) {
|
|
||||||
|
|
||||||
return PhabricatorFile::newFromFileData(
|
public function getCreateObjectTitleForFeed($author, $object) {
|
||||||
$data,
|
return pht('%s created %s.', $author, $object);
|
||||||
array(
|
|
||||||
'name' => $name,
|
|
||||||
'mime-type' => 'text/plain; charset=utf-8',
|
|
||||||
'authorPHID' => $actor->getPHID(),
|
|
||||||
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
|
||||||
'editPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getTransactionTypes() {
|
public function getTransactionTypes() {
|
||||||
$types = parent::getTransactionTypes();
|
$types = parent::getTransactionTypes();
|
||||||
|
|
||||||
$types[] = PhabricatorPasteTransaction::TYPE_CONTENT;
|
|
||||||
$types[] = PhabricatorPasteTransaction::TYPE_TITLE;
|
|
||||||
$types[] = PhabricatorPasteTransaction::TYPE_LANGUAGE;
|
|
||||||
$types[] = PhabricatorPasteTransaction::TYPE_STATUS;
|
|
||||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||||
|
@ -43,163 +29,14 @@ final class PhabricatorPasteEditor
|
||||||
return $types;
|
return $types;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function shouldApplyInitialEffects(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
array $xactions) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyInitialEffects(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
array $xactions) {
|
|
||||||
|
|
||||||
// Find the most user-friendly filename we can by examining the title of
|
|
||||||
// the paste and the pending transactions. We'll use this if we create a
|
|
||||||
// new file to store raw content later.
|
|
||||||
|
|
||||||
$name = $object->getTitle();
|
|
||||||
if (!strlen($name)) {
|
|
||||||
$name = 'paste.raw';
|
|
||||||
}
|
|
||||||
|
|
||||||
$type_title = PhabricatorPasteTransaction::TYPE_TITLE;
|
|
||||||
foreach ($xactions as $xaction) {
|
|
||||||
if ($xaction->getTransactionType() == $type_title) {
|
|
||||||
$name = $xaction->getNewValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$this->fileName = $name;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function validateTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
$type,
|
|
||||||
array $xactions) {
|
|
||||||
|
|
||||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
|
||||||
switch ($type) {
|
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
|
||||||
if (!$object->getFilePHID() && !$xactions) {
|
|
||||||
$error = new PhabricatorApplicationTransactionValidationError(
|
|
||||||
$type,
|
|
||||||
pht('Required'),
|
|
||||||
pht('You must provide content to create a paste.'),
|
|
||||||
null);
|
|
||||||
|
|
||||||
$error->setIsMissingFieldError(true);
|
|
||||||
$errors[] = $error;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $errors;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCustomTransactionOldValue(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
|
||||||
return $object->getFilePHID();
|
|
||||||
case PhabricatorPasteTransaction::TYPE_TITLE:
|
|
||||||
return $object->getTitle();
|
|
||||||
case PhabricatorPasteTransaction::TYPE_LANGUAGE:
|
|
||||||
return $object->getLanguage();
|
|
||||||
case PhabricatorPasteTransaction::TYPE_STATUS:
|
|
||||||
return $object->getStatus();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function getCustomTransactionNewValue(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case PhabricatorPasteTransaction::TYPE_TITLE:
|
|
||||||
case PhabricatorPasteTransaction::TYPE_LANGUAGE:
|
|
||||||
case PhabricatorPasteTransaction::TYPE_STATUS:
|
|
||||||
return $xaction->getNewValue();
|
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
|
||||||
// If this transaction does not really change the paste content, return
|
|
||||||
// the current file PHID so this transaction no-ops.
|
|
||||||
$new_content = $xaction->getNewValue();
|
|
||||||
$old_content = $object->getRawContent();
|
|
||||||
$file_phid = $object->getFilePHID();
|
|
||||||
if (($new_content === $old_content) && $file_phid) {
|
|
||||||
return $file_phid;
|
|
||||||
}
|
|
||||||
|
|
||||||
$file = self::initializeFileForPaste(
|
|
||||||
$this->getActor(),
|
|
||||||
$this->fileName,
|
|
||||||
$xaction->getNewValue());
|
|
||||||
|
|
||||||
return $file->getPHID();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomInternalTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
|
||||||
$object->setFilePHID($xaction->getNewValue());
|
|
||||||
return;
|
|
||||||
case PhabricatorPasteTransaction::TYPE_TITLE:
|
|
||||||
$object->setTitle($xaction->getNewValue());
|
|
||||||
return;
|
|
||||||
case PhabricatorPasteTransaction::TYPE_LANGUAGE:
|
|
||||||
$object->setLanguage($xaction->getNewValue());
|
|
||||||
return;
|
|
||||||
case PhabricatorPasteTransaction::TYPE_STATUS:
|
|
||||||
$object->setStatus($xaction->getNewValue());
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::applyCustomInternalTransaction($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function applyCustomExternalTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
|
||||||
case PhabricatorPasteTransaction::TYPE_TITLE:
|
|
||||||
case PhabricatorPasteTransaction::TYPE_LANGUAGE:
|
|
||||||
case PhabricatorPasteTransaction::TYPE_STATUS:
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::applyCustomExternalTransaction($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function extractFilePHIDsFromCustomTransaction(
|
|
||||||
PhabricatorLiskDAO $object,
|
|
||||||
PhabricatorApplicationTransaction $xaction) {
|
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
|
||||||
return array($xaction->getNewValue());
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::extractFilePHIDsFromCustomTransaction($object, $xaction);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function shouldSendMail(
|
protected function shouldSendMail(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
foreach ($xactions as $xaction) {
|
|
||||||
switch ($xaction->getTransactionType()) {
|
if ($this->getIsNewObject()) {
|
||||||
case PhabricatorPasteTransaction::TYPE_CONTENT:
|
return false;
|
||||||
return false;
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -258,8 +95,4 @@ final class PhabricatorPasteEditor
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supportsSearch() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -17,15 +17,15 @@ final class PhabricatorPasteTestDataGenerator
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
$xactions[] = $this->newTransaction(
|
$xactions[] = $this->newTransaction(
|
||||||
PhabricatorPasteTransaction::TYPE_TITLE,
|
PhabricatorPasteTitleTransaction::TRANSACTIONTYPE,
|
||||||
$name);
|
$name);
|
||||||
|
|
||||||
$xactions[] = $this->newTransaction(
|
$xactions[] = $this->newTransaction(
|
||||||
PhabricatorPasteTransaction::TYPE_LANGUAGE,
|
PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE,
|
||||||
$language);
|
$language);
|
||||||
|
|
||||||
$xactions[] = $this->newTransaction(
|
$xactions[] = $this->newTransaction(
|
||||||
PhabricatorPasteTransaction::TYPE_CONTENT,
|
PhabricatorPasteContentTransaction::TRANSACTIONTYPE,
|
||||||
$content);
|
$content);
|
||||||
|
|
||||||
$editor = id(new PhabricatorPasteEditor())
|
$editor = id(new PhabricatorPasteEditor())
|
||||||
|
|
|
@ -24,17 +24,13 @@ final class PasteCreateMailReceiver extends PhabricatorMailReceiver {
|
||||||
$xactions = array();
|
$xactions = array();
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_CONTENT)
|
->setTransactionType(PhabricatorPasteContentTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($mail->getCleanTextBody());
|
->setNewValue($mail->getCleanTextBody());
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
$xactions[] = id(new PhabricatorPasteTransaction())
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_TITLE)
|
->setTransactionType(PhabricatorPasteTitleTransaction::TRANSACTIONTYPE)
|
||||||
->setNewValue($title);
|
->setNewValue($title);
|
||||||
|
|
||||||
$xactions[] = id(new PhabricatorPasteTransaction())
|
|
||||||
->setTransactionType(PhabricatorPasteTransaction::TYPE_LANGUAGE)
|
|
||||||
->setNewValue(''); // auto-detect
|
|
||||||
|
|
||||||
$paste = PhabricatorPaste::initializeNewPaste($sender);
|
$paste = PhabricatorPaste::initializeNewPaste($sender);
|
||||||
|
|
||||||
$content_source = $mail->newContentSource();
|
$content_source = $mail->newContentSource();
|
||||||
|
|
|
@ -1,12 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
|
|
||||||
final class PhabricatorPasteTransaction
|
final class PhabricatorPasteTransaction
|
||||||
extends PhabricatorApplicationTransaction {
|
extends PhabricatorModularTransaction {
|
||||||
|
|
||||||
const TYPE_CONTENT = 'paste.create';
|
|
||||||
const TYPE_TITLE = 'paste.title';
|
|
||||||
const TYPE_LANGUAGE = 'paste.language';
|
|
||||||
const TYPE_STATUS = 'paste.status';
|
|
||||||
|
|
||||||
const MAILTAG_CONTENT = 'paste-content';
|
const MAILTAG_CONTENT = 'paste-content';
|
||||||
const MAILTAG_OTHER = 'paste-other';
|
const MAILTAG_OTHER = 'paste-other';
|
||||||
|
@ -24,226 +19,16 @@ final class PhabricatorPasteTransaction
|
||||||
return new PhabricatorPasteTransactionComment();
|
return new PhabricatorPasteTransactionComment();
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getRequiredHandlePHIDs() {
|
public function getBaseTransactionClass() {
|
||||||
$phids = parent::getRequiredHandlePHIDs();
|
return 'PhabricatorPasteTransactionType';
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
$phids[] = $this->getObjectPHID();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return $phids;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function shouldHide() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_TITLE:
|
|
||||||
case self::TYPE_LANGUAGE:
|
|
||||||
if ($old === null) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return parent::shouldHide();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getIcon() {
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
return 'fa-plus';
|
|
||||||
break;
|
|
||||||
case self::TYPE_TITLE:
|
|
||||||
case self::TYPE_LANGUAGE:
|
|
||||||
return 'fa-pencil';
|
|
||||||
break;
|
|
||||||
case self::TYPE_STATUS:
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
switch ($new) {
|
|
||||||
case PhabricatorPaste::STATUS_ACTIVE:
|
|
||||||
return 'fa-check';
|
|
||||||
case PhabricatorPaste::STATUS_ARCHIVED:
|
|
||||||
return 'fa-ban';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
return parent::getIcon();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitle() {
|
|
||||||
$author_phid = $this->getAuthorPHID();
|
|
||||||
$object_phid = $this->getObjectPHID();
|
|
||||||
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
$type = $this->getTransactionType();
|
|
||||||
switch ($type) {
|
|
||||||
case PhabricatorTransactions::TYPE_CREATE:
|
|
||||||
return pht(
|
|
||||||
'%s created this paste.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
if ($old === null) {
|
|
||||||
return pht(
|
|
||||||
'%s created this paste.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
} else {
|
|
||||||
return pht(
|
|
||||||
'%s edited the content of this paste.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::TYPE_TITLE:
|
|
||||||
return pht(
|
|
||||||
'%s updated the paste\'s title to "%s".',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$new);
|
|
||||||
break;
|
|
||||||
case self::TYPE_LANGUAGE:
|
|
||||||
return pht(
|
|
||||||
"%s updated the paste's language.",
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
break;
|
|
||||||
case self::TYPE_STATUS:
|
|
||||||
switch ($new) {
|
|
||||||
case PhabricatorPaste::STATUS_ACTIVE:
|
|
||||||
return pht(
|
|
||||||
'%s activated this paste.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
case PhabricatorPaste::STATUS_ARCHIVED:
|
|
||||||
return pht(
|
|
||||||
'%s archived this paste.',
|
|
||||||
$this->renderHandleLink($author_phid));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getTitle();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getTitleForFeed() {
|
|
||||||
$author_phid = $this->getAuthorPHID();
|
|
||||||
$object_phid = $this->getObjectPHID();
|
|
||||||
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
$type = $this->getTransactionType();
|
|
||||||
switch ($type) {
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
if ($old === null) {
|
|
||||||
return pht(
|
|
||||||
'%s created %s.',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$this->renderHandleLink($object_phid));
|
|
||||||
} else {
|
|
||||||
return pht(
|
|
||||||
'%s edited %s.',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$this->renderHandleLink($object_phid));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case self::TYPE_TITLE:
|
|
||||||
return pht(
|
|
||||||
'%s updated the title for %s.',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$this->renderHandleLink($object_phid));
|
|
||||||
break;
|
|
||||||
case self::TYPE_LANGUAGE:
|
|
||||||
return pht(
|
|
||||||
'%s updated the language for %s.',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$this->renderHandleLink($object_phid));
|
|
||||||
break;
|
|
||||||
case self::TYPE_STATUS:
|
|
||||||
switch ($new) {
|
|
||||||
case PhabricatorPaste::STATUS_ACTIVE:
|
|
||||||
return pht(
|
|
||||||
'%s activated %s.',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$this->renderHandleLink($object_phid));
|
|
||||||
case PhabricatorPaste::STATUS_ARCHIVED:
|
|
||||||
return pht(
|
|
||||||
'%s archived %s.',
|
|
||||||
$this->renderHandleLink($author_phid),
|
|
||||||
$this->renderHandleLink($object_phid));
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getTitleForFeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function getColor() {
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
return PhabricatorTransactions::COLOR_GREEN;
|
|
||||||
case self::TYPE_STATUS:
|
|
||||||
switch ($new) {
|
|
||||||
case PhabricatorPaste::STATUS_ACTIVE:
|
|
||||||
return 'green';
|
|
||||||
case PhabricatorPaste::STATUS_ARCHIVED:
|
|
||||||
return 'indigo';
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::getColor();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public function hasChangeDetails() {
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
return ($this->getOldValue() !== null);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::hasChangeDetails();
|
|
||||||
}
|
|
||||||
|
|
||||||
public function renderChangeDetails(PhabricatorUser $viewer) {
|
|
||||||
switch ($this->getTransactionType()) {
|
|
||||||
case self::TYPE_CONTENT:
|
|
||||||
$old = $this->getOldValue();
|
|
||||||
$new = $this->getNewValue();
|
|
||||||
|
|
||||||
$files = id(new PhabricatorFileQuery())
|
|
||||||
->setViewer($viewer)
|
|
||||||
->withPHIDs(array_filter(array($old, $new)))
|
|
||||||
->execute();
|
|
||||||
$files = mpull($files, null, 'getPHID');
|
|
||||||
|
|
||||||
$old_text = '';
|
|
||||||
if (idx($files, $old)) {
|
|
||||||
$old_text = $files[$old]->loadFileData();
|
|
||||||
}
|
|
||||||
|
|
||||||
$new_text = '';
|
|
||||||
if (idx($files, $new)) {
|
|
||||||
$new_text = $files[$new]->loadFileData();
|
|
||||||
}
|
|
||||||
|
|
||||||
return $this->renderTextCorpusChangeDetails(
|
|
||||||
$viewer,
|
|
||||||
$old_text,
|
|
||||||
$new_text);
|
|
||||||
}
|
|
||||||
|
|
||||||
return parent::renderChangeDetails($viewer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMailTags() {
|
public function getMailTags() {
|
||||||
$tags = array();
|
$tags = array();
|
||||||
switch ($this->getTransactionType()) {
|
switch ($this->getTransactionType()) {
|
||||||
case self::TYPE_TITLE:
|
case PhabricatorPasteTitleTransaction::TRANSACTIONTYPE:
|
||||||
case self::TYPE_CONTENT:
|
case PhabricatorPasteContentTransaction::TRANSACTIONTYPE:
|
||||||
case self::TYPE_LANGUAGE:
|
case PhabricatorPasteLanguageTransaction::TRANSACTIONTYPE:
|
||||||
$tags[] = self::MAILTAG_CONTENT;
|
$tags[] = self::MAILTAG_CONTENT;
|
||||||
break;
|
break;
|
||||||
case PhabricatorTransactions::TYPE_COMMENT:
|
case PhabricatorTransactions::TYPE_COMMENT:
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPasteContentTransaction
|
||||||
|
extends PhabricatorPasteTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'paste.create';
|
||||||
|
|
||||||
|
private $fileName;
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getFilePHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setFilePHID($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function extractFilePHIDs($object, $value) {
|
||||||
|
return array($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function validateTransactions($object, array $xactions) {
|
||||||
|
if ($object->getFilePHID() || $xactions) {
|
||||||
|
return array();
|
||||||
|
}
|
||||||
|
|
||||||
|
$error = $this->newError(
|
||||||
|
pht('Required'),
|
||||||
|
pht('You must provide content to create a paste.'));
|
||||||
|
$error->setIsMissingFieldError(true);
|
||||||
|
|
||||||
|
return array($error);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function willApplyTransactions($object, array $xactions) {
|
||||||
|
// Find the most user-friendly filename we can by examining the title of
|
||||||
|
// the paste and the pending transactions. We'll use this if we create a
|
||||||
|
// new file to store raw content later.
|
||||||
|
|
||||||
|
$name = $object->getTitle();
|
||||||
|
if (!strlen($name)) {
|
||||||
|
$name = 'paste.raw';
|
||||||
|
}
|
||||||
|
|
||||||
|
$type_title = PhabricatorPasteTitleTransaction::TRANSACTIONTYPE;
|
||||||
|
foreach ($xactions as $xaction) {
|
||||||
|
if ($xaction->getTransactionType() == $type_title) {
|
||||||
|
$name = $xaction->getNewValue();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->fileName = $name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewValue($object, $value) {
|
||||||
|
// If this transaction does not really change the paste content, return
|
||||||
|
// the current file PHID so this transaction no-ops.
|
||||||
|
$old_content = $object->getRawContent();
|
||||||
|
if ($value === $old_content) {
|
||||||
|
$file_phid = $object->getFilePHID();
|
||||||
|
if ($file_phid) {
|
||||||
|
return $file_phid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$editor = $this->getEditor();
|
||||||
|
$actor = $editor->getActor();
|
||||||
|
|
||||||
|
$file = $this->newFileForPaste($actor, $this->fileName, $value);
|
||||||
|
|
||||||
|
return $file->getPHID();
|
||||||
|
}
|
||||||
|
|
||||||
|
private function newFileForPaste(PhabricatorUser $actor, $name, $data) {
|
||||||
|
return PhabricatorFile::newFromFileData(
|
||||||
|
$data,
|
||||||
|
array(
|
||||||
|
'name' => $name,
|
||||||
|
'mime-type' => 'text/plain; charset=utf-8',
|
||||||
|
'authorPHID' => $actor->getPHID(),
|
||||||
|
'viewPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
||||||
|
'editPolicy' => PhabricatorPolicies::POLICY_NOONE,
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIcon() {
|
||||||
|
return 'fa-plus';
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
'%s edited the content of this paste.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitleForFeed() {
|
||||||
|
return pht(
|
||||||
|
'%s edited %s.',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasChangeDetailView() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function newChangeDetailView() {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$old = $this->getOldValue();
|
||||||
|
$new = $this->getNewValue();
|
||||||
|
|
||||||
|
$files = id(new PhabricatorFileQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withPHIDs(array($old, $new))
|
||||||
|
->execute();
|
||||||
|
$files = mpull($files, null, 'getPHID');
|
||||||
|
|
||||||
|
$old_text = '';
|
||||||
|
if (idx($files, $old)) {
|
||||||
|
$old_text = $files[$old]->loadFileData();
|
||||||
|
}
|
||||||
|
|
||||||
|
$new_text = '';
|
||||||
|
if (idx($files, $new)) {
|
||||||
|
$new_text = $files[$new]->loadFileData();
|
||||||
|
}
|
||||||
|
|
||||||
|
return id(new PhabricatorApplicationTransactionTextDiffDetailView())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->setOldText($old_text)
|
||||||
|
->setNewText($new_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,29 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPasteLanguageTransaction
|
||||||
|
extends PhabricatorPasteTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'paste.language';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getLanguage();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setLanguage($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
"%s updated the paste's language.",
|
||||||
|
$this->renderAuthor());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitleForFeed() {
|
||||||
|
return pht(
|
||||||
|
'%s updated the language for %s.',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,62 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPasteStatusTransaction
|
||||||
|
extends PhabricatorPasteTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'paste.status';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setStatus($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private function isActivate() {
|
||||||
|
return ($this->getNewValue() == PhabricatorPaste::STATUS_ARCHIVED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getIcon() {
|
||||||
|
if ($this->isActivate()) {
|
||||||
|
return 'fa-check';
|
||||||
|
} else {
|
||||||
|
return 'fa-ban';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getColor() {
|
||||||
|
if ($this->isActivate()) {
|
||||||
|
return 'green';
|
||||||
|
} else {
|
||||||
|
return 'indigo';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
if ($this->isActivate()) {
|
||||||
|
return pht(
|
||||||
|
'%s activated this paste.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s archived this paste.',
|
||||||
|
$this->renderAuthor());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitleForFeed() {
|
||||||
|
if ($this->isActivate()) {
|
||||||
|
return pht(
|
||||||
|
'%s activated %s.',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderObject());
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s archived %s.',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderObject());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,33 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorPasteTitleTransaction
|
||||||
|
extends PhabricatorPasteTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'paste.title';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return $object->getTitle();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyInternalEffects($object, $value) {
|
||||||
|
$object->setTitle($value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitle() {
|
||||||
|
return pht(
|
||||||
|
'%s updated the paste\'s title from "%s" to "%s".',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->getOldValue(),
|
||||||
|
$this->getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTitleForFeed() {
|
||||||
|
return pht(
|
||||||
|
'%s updated the title for %s from "%s" to "%s".',
|
||||||
|
$this->renderAuthor(),
|
||||||
|
$this->renderObject(),
|
||||||
|
$this->getOldValue(),
|
||||||
|
$this->getNewValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,4 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
abstract class PhabricatorPasteTransactionType
|
||||||
|
extends PhabricatorModularTransactionType {}
|
|
@ -29,21 +29,28 @@ final class PhabricatorUserPreferencesCacheType
|
||||||
|
|
||||||
$preferences = id(new PhabricatorUserPreferencesQuery())
|
$preferences = id(new PhabricatorUserPreferencesQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withUserPHIDs($user_phids)
|
->withUsers($users)
|
||||||
|
->needSyntheticPreferences(true)
|
||||||
->execute();
|
->execute();
|
||||||
|
$preferences = mpull($preferences, null, 'getUserPHID');
|
||||||
|
|
||||||
$all_settings = PhabricatorSetting::getAllSettings();
|
$all_settings = PhabricatorSetting::getAllSettings();
|
||||||
|
|
||||||
$settings = array();
|
$settings = array();
|
||||||
foreach ($preferences as $preference) {
|
foreach ($users as $user_phid => $user) {
|
||||||
$user_phid = $preference->getUserPHID();
|
$preference = idx($preferences, $user_phid);
|
||||||
|
|
||||||
|
if (!$preference) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
foreach ($all_settings as $key => $setting) {
|
foreach ($all_settings as $key => $setting) {
|
||||||
$value = $preference->getSettingValue($key);
|
$value = $preference->getSettingValue($key);
|
||||||
|
|
||||||
// As an optimization, we omit the value from the cache if it is
|
// As an optimization, we omit the value from the cache if it is
|
||||||
// exactly the same as the hardcoded default.
|
// exactly the same as the hardcoded default.
|
||||||
$default_value = id(clone $setting)
|
$default_value = id(clone $setting)
|
||||||
->setViewer($users[$user_phid])
|
->setViewer($user)
|
||||||
->getSettingDefaultValue();
|
->getSettingDefaultValue();
|
||||||
if ($value === $default_value) {
|
if ($value === $default_value) {
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -55,6 +55,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
||||||
'preview/' => 'PhabricatorMarkupPreviewController',
|
'preview/' => 'PhabricatorMarkupPreviewController',
|
||||||
'framed/(?P<id>\d+)/' => 'PhamePostFramedController',
|
'framed/(?P<id>\d+)/' => 'PhamePostFramedController',
|
||||||
'move/(?P<id>\d+)/' => 'PhamePostMoveController',
|
'move/(?P<id>\d+)/' => 'PhamePostMoveController',
|
||||||
|
'archive/(?P<id>\d+)/' => 'PhamePostArchiveController',
|
||||||
'comment/(?P<id>[1-9]\d*)/' => 'PhamePostCommentController',
|
'comment/(?P<id>[1-9]\d*)/' => 'PhamePostCommentController',
|
||||||
),
|
),
|
||||||
'blog/' => array(
|
'blog/' => array(
|
||||||
|
@ -66,6 +67,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication {
|
||||||
'manage/(?P<id>[^/]+)/' => 'PhameBlogManageController',
|
'manage/(?P<id>[^/]+)/' => 'PhameBlogManageController',
|
||||||
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
|
'feed/(?P<id>[^/]+)/' => 'PhameBlogFeedController',
|
||||||
'picture/(?P<id>[1-9]\d*)/' => 'PhameBlogProfilePictureController',
|
'picture/(?P<id>[1-9]\d*)/' => 'PhameBlogProfilePictureController',
|
||||||
|
'header/(?P<id>[1-9]\d*)/' => 'PhameBlogHeaderPictureController',
|
||||||
),
|
),
|
||||||
) + $this->getResourceSubroutes(),
|
) + $this->getResourceSubroutes(),
|
||||||
);
|
);
|
||||||
|
|
|
@ -2,13 +2,15 @@
|
||||||
|
|
||||||
final class PhameConstants extends Phobject {
|
final class PhameConstants extends Phobject {
|
||||||
|
|
||||||
const VISIBILITY_DRAFT = 0;
|
const VISIBILITY_DRAFT = 0;
|
||||||
const VISIBILITY_PUBLISHED = 1;
|
const VISIBILITY_PUBLISHED = 1;
|
||||||
|
const VISIBILITY_ARCHIVED = 2;
|
||||||
|
|
||||||
public static function getPhamePostStatusMap() {
|
public static function getPhamePostStatusMap() {
|
||||||
return array(
|
return array(
|
||||||
self::VISIBILITY_PUBLISHED => pht('Published'),
|
self::VISIBILITY_PUBLISHED => pht('Published'),
|
||||||
self::VISIBILITY_DRAFT => pht('Draft'),
|
self::VISIBILITY_DRAFT => pht('Draft'),
|
||||||
|
self::VISIBILITY_ARCHIVED => pht('Archived'),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -16,6 +18,7 @@ final class PhameConstants extends Phobject {
|
||||||
$map = array(
|
$map = array(
|
||||||
self::VISIBILITY_PUBLISHED => pht('Published'),
|
self::VISIBILITY_PUBLISHED => pht('Published'),
|
||||||
self::VISIBILITY_DRAFT => pht('Draft'),
|
self::VISIBILITY_DRAFT => pht('Draft'),
|
||||||
|
self::VISIBILITY_ARCHIVED => pht('Archived'),
|
||||||
);
|
);
|
||||||
return idx($map, $status, pht('Unknown'));
|
return idx($map, $status, pht('Unknown'));
|
||||||
}
|
}
|
||||||
|
|
|
@ -35,7 +35,8 @@ final class PhameHomeController extends PhamePostController {
|
||||||
$posts = id(new PhamePostQuery())
|
$posts = id(new PhamePostQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBlogPHIDs($blog_phids)
|
->withBlogPHIDs($blog_phids)
|
||||||
->withVisibility(PhameConstants::VISIBILITY_PUBLISHED)
|
->withVisibility(array(PhameConstants::VISIBILITY_PUBLISHED))
|
||||||
|
->setOrder('datePublished')
|
||||||
->executeWithCursorPager($pager);
|
->executeWithCursorPager($pager);
|
||||||
|
|
||||||
if ($posts) {
|
if ($posts) {
|
||||||
|
@ -97,7 +98,7 @@ final class PhameHomeController extends PhamePostController {
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBloggerPHIDs(array($viewer->getPHID()))
|
->withBloggerPHIDs(array($viewer->getPHID()))
|
||||||
->withBlogPHIDs(mpull($blogs, 'getPHID'))
|
->withBlogPHIDs(mpull($blogs, 'getPHID'))
|
||||||
->withVisibility(PhameConstants::VISIBILITY_DRAFT)
|
->withVisibility(array(PhameConstants::VISIBILITY_DRAFT))
|
||||||
->setLimit(5)
|
->setLimit(5)
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
|
|
|
@ -58,6 +58,7 @@ abstract class PhameLiveController extends PhameController {
|
||||||
$blog_query = id(new PhameBlogQuery())
|
$blog_query = id(new PhameBlogQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->needProfileImage(true)
|
->needProfileImage(true)
|
||||||
|
->needHeaderImage(true)
|
||||||
->withIDs(array($blog_id));
|
->withIDs(array($blog_id));
|
||||||
|
|
||||||
// If this is a live view, only show active blogs.
|
// If this is a live view, only show active blogs.
|
||||||
|
@ -97,7 +98,8 @@ abstract class PhameLiveController extends PhameController {
|
||||||
|
|
||||||
// Only show published posts on external domains.
|
// Only show published posts on external domains.
|
||||||
if ($is_external) {
|
if ($is_external) {
|
||||||
$post_query->withVisibility(PhameConstants::VISIBILITY_PUBLISHED);
|
$post_query->withVisibility(
|
||||||
|
array(PhameConstants::VISIBILITY_PUBLISHED));
|
||||||
}
|
}
|
||||||
|
|
||||||
$post = $post_query->executeOne();
|
$post = $post_query->executeOne();
|
||||||
|
@ -210,6 +212,7 @@ abstract class PhameLiveController extends PhameController {
|
||||||
|
|
||||||
if ($this->getIsLive()) {
|
if ($this->getIsLive()) {
|
||||||
$page
|
$page
|
||||||
|
->addClass('phame-live-view')
|
||||||
->setShowChrome(false)
|
->setShowChrome(false)
|
||||||
->setShowFooter(false);
|
->setShowFooter(false);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,7 @@ final class PhameBlogFeedController extends PhameBlogController {
|
||||||
$posts = id(new PhamePostQuery())
|
$posts = id(new PhamePostQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBlogPHIDs(array($blog->getPHID()))
|
->withBlogPHIDs(array($blog->getPHID()))
|
||||||
->withVisibility(PhameConstants::VISIBILITY_PUBLISHED)
|
->withVisibility(array(PhameConstants::VISIBILITY_PUBLISHED))
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
$blog_uri = PhabricatorEnv::getProductionURI(
|
$blog_uri = PhabricatorEnv::getProductionURI(
|
||||||
|
|
|
@ -0,0 +1,126 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhameBlogHeaderPictureController
|
||||||
|
extends PhameBlogController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
|
||||||
|
$blog = id(new PhameBlogQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->needHeaderImage(true)
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$blog) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$blog_uri = '/phame/blog/manage/'.$id;
|
||||||
|
|
||||||
|
$supported_formats = PhabricatorFile::getTransformableImageFormats();
|
||||||
|
$e_file = true;
|
||||||
|
$errors = array();
|
||||||
|
$delete_header = ($request->getInt('delete') == 1);
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
if ($request->getFileExists('header')) {
|
||||||
|
$file = PhabricatorFile::newFromPHPUpload(
|
||||||
|
$_FILES['header'],
|
||||||
|
array(
|
||||||
|
'authorPHID' => $viewer->getPHID(),
|
||||||
|
'canCDN' => true,
|
||||||
|
));
|
||||||
|
} else if (!$delete_header) {
|
||||||
|
$e_file = pht('Required');
|
||||||
|
$errors[] = pht(
|
||||||
|
'You must choose a file when uploading a new blog header.');
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$errors && !$delete_header) {
|
||||||
|
if (!$file->isTransformableImage()) {
|
||||||
|
$e_file = pht('Not Supported');
|
||||||
|
$errors[] = pht(
|
||||||
|
'This server only supports these image formats: %s.',
|
||||||
|
implode(', ', $supported_formats));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$errors) {
|
||||||
|
if ($delete_header) {
|
||||||
|
$blog->setHeaderImagePHID(null);
|
||||||
|
} else {
|
||||||
|
$blog->setHeaderImagePHID($file->getPHID());
|
||||||
|
$file->attachToObject($blog->getPHID());
|
||||||
|
}
|
||||||
|
$blog->save();
|
||||||
|
return id(new AphrontRedirectResponse())->setURI($blog_uri);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = pht('Edit Blog Header');
|
||||||
|
|
||||||
|
$upload_form = id(new AphrontFormView())
|
||||||
|
->setUser($viewer)
|
||||||
|
->setEncType('multipart/form-data')
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormFileControl())
|
||||||
|
->setName('header')
|
||||||
|
->setLabel(pht('Upload Header'))
|
||||||
|
->setError($e_file)
|
||||||
|
->setCaption(
|
||||||
|
pht('Supported formats: %s', implode(', ', $supported_formats))))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormCheckboxControl())
|
||||||
|
->setName('delete')
|
||||||
|
->setLabel(pht('Delete Header'))
|
||||||
|
->addCheckbox(
|
||||||
|
'delete',
|
||||||
|
1,
|
||||||
|
null,
|
||||||
|
null))
|
||||||
|
->appendChild(
|
||||||
|
id(new AphrontFormSubmitControl())
|
||||||
|
->addCancelButton($blog_uri)
|
||||||
|
->setValue(pht('Upload Header')));
|
||||||
|
|
||||||
|
$upload_box = id(new PHUIObjectBoxView())
|
||||||
|
->setHeaderText(pht('Upload New Header'))
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
|
->setForm($upload_form);
|
||||||
|
|
||||||
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
pht('Blogs'),
|
||||||
|
$this->getApplicationURI('blog/'));
|
||||||
|
$crumbs->addTextCrumb(
|
||||||
|
$blog->getName(),
|
||||||
|
$this->getApplicationURI('blog/view/'.$id));
|
||||||
|
$crumbs->addTextCrumb(pht('Blog Header'));
|
||||||
|
$crumbs->setBorder(true);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Edit Blog Header'))
|
||||||
|
->setHeaderIcon('fa-camera');
|
||||||
|
|
||||||
|
$view = id(new PHUITwoColumnView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setFooter(array(
|
||||||
|
$upload_box,
|
||||||
|
));
|
||||||
|
|
||||||
|
return $this->newPage()
|
||||||
|
->setTitle($title)
|
||||||
|
->setCrumbs($crumbs)
|
||||||
|
->appendChild(
|
||||||
|
array(
|
||||||
|
$view,
|
||||||
|
));
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
|
@ -14,6 +14,7 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withIDs(array($id))
|
->withIDs(array($id))
|
||||||
->needProfileImage(true)
|
->needProfileImage(true)
|
||||||
|
->needHeaderImage(true)
|
||||||
->executeOne();
|
->executeOne();
|
||||||
if (!$blog) {
|
if (!$blog) {
|
||||||
return new Aphront404Response();
|
return new Aphront404Response();
|
||||||
|
@ -31,22 +32,33 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
|
|
||||||
$picture = $blog->getProfileImageURI();
|
$picture = $blog->getProfileImageURI();
|
||||||
|
|
||||||
|
$view = id(new PHUIButtonView())
|
||||||
|
->setTag('a')
|
||||||
|
->setText(pht('View Live'))
|
||||||
|
->setIcon('fa-external-link')
|
||||||
|
->setHref($blog->getLiveURI());
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($blog->getName())
|
->setHeader($blog->getName())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setPolicyObject($blog)
|
->setPolicyObject($blog)
|
||||||
->setImage($picture)
|
->setImage($picture)
|
||||||
->setStatus($header_icon, $header_color, $header_name);
|
->setStatus($header_icon, $header_color, $header_name)
|
||||||
|
->addActionLink($view);
|
||||||
|
|
||||||
$actions = $this->renderActions($blog, $viewer);
|
$curtain = $this->buildCurtain($blog);
|
||||||
$properties = $this->renderProperties($blog, $viewer, $actions);
|
$properties = $this->buildPropertyView($blog);
|
||||||
|
$file = $this->buildFileView($blog);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
pht('Blogs'),
|
pht('Blogs'),
|
||||||
$this->getApplicationURI('blog/'));
|
$this->getApplicationURI('blog/'));
|
||||||
$crumbs->addTextCrumb(
|
$crumbs->addTextCrumb(
|
||||||
$blog->getName());
|
$blog->getName(),
|
||||||
|
$this->getApplicationURI('blog/view/'.$id));
|
||||||
|
$crumbs->addTextCrumb(pht('Manage Blog'));
|
||||||
|
$crumbs->setBorder(true);
|
||||||
|
|
||||||
$object_box = id(new PHUIObjectBoxView())
|
$object_box = id(new PHUIObjectBoxView())
|
||||||
->setHeader($header)
|
->setHeader($header)
|
||||||
|
@ -57,28 +69,34 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
new PhameBlogTransactionQuery());
|
new PhameBlogTransactionQuery());
|
||||||
$timeline->setShouldTerminate(true);
|
$timeline->setShouldTerminate(true);
|
||||||
|
|
||||||
|
$view = id(new PHUITwoColumnView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setCurtain($curtain)
|
||||||
|
->addPropertySection(pht('Details'), $properties)
|
||||||
|
->addPropertySection(pht('Header'), $file)
|
||||||
|
->setMainColumn(
|
||||||
|
array(
|
||||||
|
$timeline,
|
||||||
|
));
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($blog->getName())
|
->setTitle($blog->getName())
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
$object_box,
|
$view,
|
||||||
$timeline,
|
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderProperties(
|
private function buildPropertyView(PhameBlog $blog) {
|
||||||
PhameBlog $blog,
|
$viewer = $this->getViewer();
|
||||||
PhabricatorUser $viewer,
|
|
||||||
PhabricatorActionListView $actions) {
|
|
||||||
|
|
||||||
require_celerity_resource('aphront-tooltip-css');
|
require_celerity_resource('aphront-tooltip-css');
|
||||||
Javelin::initBehavior('phabricator-tooltips');
|
Javelin::initBehavior('phabricator-tooltips');
|
||||||
|
|
||||||
$properties = id(new PHUIPropertyListView())
|
$properties = id(new PHUIPropertyListView())
|
||||||
->setUser($viewer)
|
->setUser($viewer)
|
||||||
->setObject($blog)
|
->setObject($blog);
|
||||||
->setActionList($actions);
|
|
||||||
|
|
||||||
$domain = $blog->getDomain();
|
$domain = $blog->getDomain();
|
||||||
if (!$domain) {
|
if (!$domain) {
|
||||||
|
@ -129,7 +147,11 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
return $properties;
|
return $properties;
|
||||||
}
|
}
|
||||||
|
|
||||||
private function renderActions(PhameBlog $blog, PhabricatorUser $viewer) {
|
private function buildCurtain(PhameBlog $blog) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$curtain = $this->newCurtainView($viewer);
|
||||||
|
|
||||||
$actions = id(new PhabricatorActionListView())
|
$actions = id(new PhabricatorActionListView())
|
||||||
->setObject($blog)
|
->setObject($blog)
|
||||||
->setUser($viewer);
|
->setUser($viewer);
|
||||||
|
@ -139,7 +161,7 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
$blog,
|
$blog,
|
||||||
PhabricatorPolicyCapability::CAN_EDIT);
|
PhabricatorPolicyCapability::CAN_EDIT);
|
||||||
|
|
||||||
$actions->addAction(
|
$curtain->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setIcon('fa-pencil')
|
->setIcon('fa-pencil')
|
||||||
->setHref($this->getApplicationURI('blog/edit/'.$blog->getID().'/'))
|
->setHref($this->getApplicationURI('blog/edit/'.$blog->getID().'/'))
|
||||||
|
@ -147,7 +169,15 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
->setDisabled(!$can_edit)
|
->setDisabled(!$can_edit)
|
||||||
->setWorkflow(!$can_edit));
|
->setWorkflow(!$can_edit));
|
||||||
|
|
||||||
$actions->addAction(
|
$curtain->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setIcon('fa-camera')
|
||||||
|
->setHref($this->getApplicationURI('blog/header/'.$blog->getID().'/'))
|
||||||
|
->setName(pht('Edit Blog Header'))
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(!$can_edit));
|
||||||
|
|
||||||
|
$curtain->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setIcon('fa-picture-o')
|
->setIcon('fa-picture-o')
|
||||||
->setHref($this->getApplicationURI('blog/picture/'.$blog->getID().'/'))
|
->setHref($this->getApplicationURI('blog/picture/'.$blog->getID().'/'))
|
||||||
|
@ -156,7 +186,7 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
->setWorkflow(!$can_edit));
|
->setWorkflow(!$can_edit));
|
||||||
|
|
||||||
if ($blog->isArchived()) {
|
if ($blog->isArchived()) {
|
||||||
$actions->addAction(
|
$curtain->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setName(pht('Activate Blog'))
|
->setName(pht('Activate Blog'))
|
||||||
->setIcon('fa-check')
|
->setIcon('fa-check')
|
||||||
|
@ -165,7 +195,7 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
->setDisabled(!$can_edit)
|
->setDisabled(!$can_edit)
|
||||||
->setWorkflow(true));
|
->setWorkflow(true));
|
||||||
} else {
|
} else {
|
||||||
$actions->addAction(
|
$curtain->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
->setName(pht('Archive Blog'))
|
->setName(pht('Archive Blog'))
|
||||||
->setIcon('fa-ban')
|
->setIcon('fa-ban')
|
||||||
|
@ -175,7 +205,27 @@ final class PhameBlogManageController extends PhameBlogController {
|
||||||
->setWorkflow(true));
|
->setWorkflow(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $actions;
|
return $curtain;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function buildFileView(
|
||||||
|
PhameBlog $blog) {
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$view = id(new PHUIPropertyListView())
|
||||||
|
->setUser($viewer);
|
||||||
|
|
||||||
|
if ($blog->getHeaderImagePHID()) {
|
||||||
|
$view->addImageContent(
|
||||||
|
phutil_tag(
|
||||||
|
'img',
|
||||||
|
array(
|
||||||
|
'src' => $blog->getHeaderImageURI(),
|
||||||
|
'class' => 'phabricator-image-macro-hero',
|
||||||
|
)));
|
||||||
|
return $view;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,10 +3,6 @@
|
||||||
final class PhameBlogProfilePictureController
|
final class PhameBlogProfilePictureController
|
||||||
extends PhameBlogController {
|
extends PhameBlogController {
|
||||||
|
|
||||||
public function shouldRequireAdmin() {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public function handleRequest(AphrontRequest $request) {
|
public function handleRequest(AphrontRequest $request) {
|
||||||
$viewer = $request->getViewer();
|
$viewer = $request->getViewer();
|
||||||
$id = $request->getURIData('id');
|
$id = $request->getURIData('id');
|
||||||
|
@ -175,6 +171,7 @@ final class PhameBlogProfilePictureController
|
||||||
$form_box = id(new PHUIObjectBoxView())
|
$form_box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText($title)
|
->setHeaderText($title)
|
||||||
->setFormErrors($errors)
|
->setFormErrors($errors)
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->setForm($form);
|
->setForm($form);
|
||||||
|
|
||||||
$upload_form = id(new AphrontFormView())
|
$upload_form = id(new AphrontFormView())
|
||||||
|
@ -194,6 +191,7 @@ final class PhameBlogProfilePictureController
|
||||||
|
|
||||||
$upload_box = id(new PHUIObjectBoxView())
|
$upload_box = id(new PHUIObjectBoxView())
|
||||||
->setHeaderText(pht('Upload New Picture'))
|
->setHeaderText(pht('Upload New Picture'))
|
||||||
|
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||||
->setForm($upload_form);
|
->setForm($upload_form);
|
||||||
|
|
||||||
$crumbs = $this->buildApplicationCrumbs();
|
$crumbs = $this->buildApplicationCrumbs();
|
||||||
|
@ -204,14 +202,25 @@ final class PhameBlogProfilePictureController
|
||||||
$blog->getName(),
|
$blog->getName(),
|
||||||
$this->getApplicationURI('blog/view/'.$id));
|
$this->getApplicationURI('blog/view/'.$id));
|
||||||
$crumbs->addTextCrumb(pht('Blog Picture'));
|
$crumbs->addTextCrumb(pht('Blog Picture'));
|
||||||
|
$crumbs->setBorder(true);
|
||||||
|
|
||||||
|
$header = id(new PHUIHeaderView())
|
||||||
|
->setHeader(pht('Edit Blog Picture'))
|
||||||
|
->setHeaderIcon('fa-camera');
|
||||||
|
|
||||||
|
$view = id(new PHUITwoColumnView())
|
||||||
|
->setHeader($header)
|
||||||
|
->setFooter(array(
|
||||||
|
$form_box,
|
||||||
|
$upload_box,
|
||||||
|
));
|
||||||
|
|
||||||
return $this->newPage()
|
return $this->newPage()
|
||||||
->setTitle($title)
|
->setTitle($title)
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
$form_box,
|
$view,
|
||||||
$upload_box,
|
|
||||||
));
|
));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,16 +19,23 @@ final class PhameBlogViewController extends PhameLiveController {
|
||||||
|
|
||||||
$post_query = id(new PhamePostQuery())
|
$post_query = id(new PhamePostQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withBlogPHIDs(array($blog->getPHID()));
|
->withBlogPHIDs(array($blog->getPHID()))
|
||||||
|
->setOrder('datePublished')
|
||||||
|
->withVisibility(array(
|
||||||
|
PhameConstants::VISIBILITY_PUBLISHED,
|
||||||
|
PhameConstants::VISIBILITY_DRAFT,
|
||||||
|
));
|
||||||
|
|
||||||
if ($is_live) {
|
if ($is_live) {
|
||||||
$post_query->withVisibility(PhameConstants::VISIBILITY_PUBLISHED);
|
$post_query->withVisibility(array(PhameConstants::VISIBILITY_PUBLISHED));
|
||||||
}
|
}
|
||||||
|
|
||||||
$posts = $post_query->executeWithCursorPager($pager);
|
$posts = $post_query->executeWithCursorPager($pager);
|
||||||
|
|
||||||
|
$hero = $this->buildPhameHeader($blog);
|
||||||
|
|
||||||
$header = id(new PHUIHeaderView())
|
$header = id(new PHUIHeaderView())
|
||||||
->setHeader($blog->getName())
|
->addClass('phame-header-bar')
|
||||||
->setUser($viewer);
|
->setUser($viewer);
|
||||||
|
|
||||||
if (!$is_external) {
|
if (!$is_external) {
|
||||||
|
@ -104,6 +111,7 @@ final class PhameBlogViewController extends PhameLiveController {
|
||||||
->setCrumbs($crumbs)
|
->setCrumbs($crumbs)
|
||||||
->appendChild(
|
->appendChild(
|
||||||
array(
|
array(
|
||||||
|
$hero,
|
||||||
$page,
|
$page,
|
||||||
$about,
|
$about,
|
||||||
));
|
));
|
||||||
|
@ -147,4 +155,33 @@ final class PhameBlogViewController extends PhameLiveController {
|
||||||
return $actions;
|
return $actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function buildPhameHeader(
|
||||||
|
PhameBlog $blog) {
|
||||||
|
|
||||||
|
$image = null;
|
||||||
|
if ($blog->getHeaderImagePHID()) {
|
||||||
|
$image = phutil_tag(
|
||||||
|
'div',
|
||||||
|
array(
|
||||||
|
'class' => 'phame-header-hero',
|
||||||
|
),
|
||||||
|
phutil_tag(
|
||||||
|
'img',
|
||||||
|
array(
|
||||||
|
'src' => $blog->getHeaderImageURI(),
|
||||||
|
'class' => 'phame-header-image',
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = phutil_tag_div('phame-header-title', $blog->getName());
|
||||||
|
$subtitle = null;
|
||||||
|
if ($blog->getSubtitle()) {
|
||||||
|
$subtitle = phutil_tag_div('phame-header-subtitle', $blog->getSubtitle());
|
||||||
|
}
|
||||||
|
|
||||||
|
return phutil_tag_div(
|
||||||
|
'phame-mega-header', array($image, $title, $subtitle));
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,56 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhamePostArchiveController extends PhamePostController {
|
||||||
|
|
||||||
|
public function handleRequest(AphrontRequest $request) {
|
||||||
|
$viewer = $request->getViewer();
|
||||||
|
|
||||||
|
$id = $request->getURIData('id');
|
||||||
|
$post = id(new PhamePostQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withIDs(array($id))
|
||||||
|
->requireCapabilities(
|
||||||
|
array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
PhabricatorPolicyCapability::CAN_EDIT,
|
||||||
|
))
|
||||||
|
->executeOne();
|
||||||
|
if (!$post) {
|
||||||
|
return new Aphront404Response();
|
||||||
|
}
|
||||||
|
|
||||||
|
$cancel_uri = $post->getViewURI();
|
||||||
|
|
||||||
|
if ($request->isFormPost()) {
|
||||||
|
$xactions = array();
|
||||||
|
|
||||||
|
$new_value = PhameConstants::VISIBILITY_ARCHIVED;
|
||||||
|
$xactions[] = id(new PhamePostTransaction())
|
||||||
|
->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY)
|
||||||
|
->setNewValue($new_value);
|
||||||
|
|
||||||
|
id(new PhamePostEditor())
|
||||||
|
->setActor($viewer)
|
||||||
|
->setContentSourceFromRequest($request)
|
||||||
|
->setContinueOnNoEffect(true)
|
||||||
|
->setContinueOnMissingFields(true)
|
||||||
|
->applyTransactions($post, $xactions);
|
||||||
|
|
||||||
|
return id(new AphrontRedirectResponse())
|
||||||
|
->setURI($cancel_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
$title = pht('Archive Post');
|
||||||
|
$body = pht(
|
||||||
|
'This post will revert to archived status and no longer be visible '.
|
||||||
|
'to other users or members of this blog.');
|
||||||
|
$button = pht('Archive Post');
|
||||||
|
|
||||||
|
return $this->newDialog()
|
||||||
|
->setTitle($title)
|
||||||
|
->appendParagraph($body)
|
||||||
|
->addSubmitButton($button)
|
||||||
|
->addCancelButton($cancel_uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -48,6 +48,16 @@ final class PhamePostViewController
|
||||||
'Use "Publish" to publish this post.')));
|
'Use "Publish" to publish this post.')));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($post->isArchived()) {
|
||||||
|
$document->appendChild(
|
||||||
|
id(new PHUIInfoView())
|
||||||
|
->setSeverity(PHUIInfoView::SEVERITY_ERROR)
|
||||||
|
->setTitle(pht('Archived Post'))
|
||||||
|
->appendChild(
|
||||||
|
pht('Only you can see this archived post until you publish it. '.
|
||||||
|
'Use "Publish" to publish this post.')));
|
||||||
|
}
|
||||||
|
|
||||||
if (!$post->getBlog()) {
|
if (!$post->getBlog()) {
|
||||||
$document->appendChild(
|
$document->appendChild(
|
||||||
id(new PHUIInfoView())
|
id(new PHUIInfoView())
|
||||||
|
@ -92,6 +102,8 @@ final class PhamePostViewController
|
||||||
$date = phabricator_datetime($post->getDatePublished(), $viewer);
|
$date = phabricator_datetime($post->getDatePublished(), $viewer);
|
||||||
if ($post->isDraft()) {
|
if ($post->isDraft()) {
|
||||||
$subtitle = pht('Unpublished draft by %s.', $author);
|
$subtitle = pht('Unpublished draft by %s.', $author);
|
||||||
|
} else if ($post->isArchived()) {
|
||||||
|
$subtitle = pht('Archived post by %s.', $author);
|
||||||
} else {
|
} else {
|
||||||
$subtitle = pht('Written by %s on %s.', $author, $date);
|
$subtitle = pht('Written by %s on %s.', $author, $date);
|
||||||
}
|
}
|
||||||
|
@ -207,6 +219,21 @@ final class PhamePostViewController
|
||||||
->setName(pht('Publish'))
|
->setName(pht('Publish'))
|
||||||
->setDisabled(!$can_edit)
|
->setDisabled(!$can_edit)
|
||||||
->setWorkflow(true));
|
->setWorkflow(true));
|
||||||
|
$actions->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setIcon('fa-ban')
|
||||||
|
->setHref($this->getApplicationURI('post/archive/'.$id.'/'))
|
||||||
|
->setName(pht('Archive'))
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(true));
|
||||||
|
} else if ($post->isArchived()) {
|
||||||
|
$actions->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setIcon('fa-eye')
|
||||||
|
->setHref($this->getApplicationURI('post/publish/'.$id.'/'))
|
||||||
|
->setName(pht('Publish'))
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(true));
|
||||||
} else {
|
} else {
|
||||||
$actions->addAction(
|
$actions->addAction(
|
||||||
id(new PhabricatorActionView())
|
id(new PhabricatorActionView())
|
||||||
|
@ -215,6 +242,13 @@ final class PhamePostViewController
|
||||||
->setName(pht('Unpublish'))
|
->setName(pht('Unpublish'))
|
||||||
->setDisabled(!$can_edit)
|
->setDisabled(!$can_edit)
|
||||||
->setWorkflow(true));
|
->setWorkflow(true));
|
||||||
|
$actions->addAction(
|
||||||
|
id(new PhabricatorActionView())
|
||||||
|
->setIcon('fa-ban')
|
||||||
|
->setHref($this->getApplicationURI('post/archive/'.$id.'/'))
|
||||||
|
->setName(pht('Archive'))
|
||||||
|
->setDisabled(!$can_edit)
|
||||||
|
->setWorkflow(true));
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($post->isDraft()) {
|
if ($post->isDraft()) {
|
||||||
|
@ -223,12 +257,14 @@ final class PhamePostViewController
|
||||||
$live_name = pht('View Live');
|
$live_name = pht('View Live');
|
||||||
}
|
}
|
||||||
|
|
||||||
$actions->addAction(
|
if (!$post->isArchived()) {
|
||||||
id(new PhabricatorActionView())
|
$actions->addAction(
|
||||||
->setUser($viewer)
|
id(new PhabricatorActionView())
|
||||||
->setIcon('fa-globe')
|
->setUser($viewer)
|
||||||
->setHref($post->getLiveURI())
|
->setIcon('fa-globe')
|
||||||
->setName($live_name));
|
->setHref($post->getLiveURI())
|
||||||
|
->setName($live_name));
|
||||||
|
}
|
||||||
|
|
||||||
return $actions;
|
return $actions;
|
||||||
}
|
}
|
||||||
|
@ -255,7 +291,7 @@ final class PhamePostViewController
|
||||||
|
|
||||||
$query = id(new PhamePostQuery())
|
$query = id(new PhamePostQuery())
|
||||||
->setViewer($viewer)
|
->setViewer($viewer)
|
||||||
->withVisibility(PhameConstants::VISIBILITY_PUBLISHED)
|
->withVisibility(array(PhameConstants::VISIBILITY_PUBLISHED))
|
||||||
->withBlogPHIDs(array($post->getBlog()->getPHID()))
|
->withBlogPHIDs(array($post->getBlog()->getPHID()))
|
||||||
->setLimit(1);
|
->setLimit(1);
|
||||||
|
|
||||||
|
|
|
@ -77,6 +77,14 @@ final class PhameBlogEditEngine
|
||||||
->setConduitTypeDescription(pht('New blog title.'))
|
->setConduitTypeDescription(pht('New blog title.'))
|
||||||
->setTransactionType(PhameBlogTransaction::TYPE_NAME)
|
->setTransactionType(PhameBlogTransaction::TYPE_NAME)
|
||||||
->setValue($object->getName()),
|
->setValue($object->getName()),
|
||||||
|
id(new PhabricatorTextEditField())
|
||||||
|
->setKey('subtitle')
|
||||||
|
->setLabel(pht('Subtitle'))
|
||||||
|
->setDescription(pht('Blog subtitle.'))
|
||||||
|
->setConduitDescription(pht('Change the blog subtitle.'))
|
||||||
|
->setConduitTypeDescription(pht('New blog subtitle.'))
|
||||||
|
->setTransactionType(PhameBlogTransaction::TYPE_SUBTITLE)
|
||||||
|
->setValue($object->getSubtitle()),
|
||||||
id(new PhabricatorRemarkupEditField())
|
id(new PhabricatorRemarkupEditField())
|
||||||
->setKey('description')
|
->setKey('description')
|
||||||
->setLabel(pht('Description'))
|
->setLabel(pht('Description'))
|
||||||
|
|
|
@ -15,6 +15,7 @@ final class PhameBlogEditor
|
||||||
$types = parent::getTransactionTypes();
|
$types = parent::getTransactionTypes();
|
||||||
|
|
||||||
$types[] = PhameBlogTransaction::TYPE_NAME;
|
$types[] = PhameBlogTransaction::TYPE_NAME;
|
||||||
|
$types[] = PhameBlogTransaction::TYPE_SUBTITLE;
|
||||||
$types[] = PhameBlogTransaction::TYPE_DESCRIPTION;
|
$types[] = PhameBlogTransaction::TYPE_DESCRIPTION;
|
||||||
$types[] = PhameBlogTransaction::TYPE_DOMAIN;
|
$types[] = PhameBlogTransaction::TYPE_DOMAIN;
|
||||||
$types[] = PhameBlogTransaction::TYPE_STATUS;
|
$types[] = PhameBlogTransaction::TYPE_STATUS;
|
||||||
|
@ -31,6 +32,8 @@ final class PhameBlogEditor
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhameBlogTransaction::TYPE_NAME:
|
case PhameBlogTransaction::TYPE_NAME:
|
||||||
return $object->getName();
|
return $object->getName();
|
||||||
|
case PhameBlogTransaction::TYPE_SUBTITLE:
|
||||||
|
return $object->getSubtitle();
|
||||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||||
return $object->getDescription();
|
return $object->getDescription();
|
||||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||||
|
@ -46,6 +49,7 @@ final class PhameBlogEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhameBlogTransaction::TYPE_NAME:
|
case PhameBlogTransaction::TYPE_NAME:
|
||||||
|
case PhameBlogTransaction::TYPE_SUBTITLE:
|
||||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||||
case PhameBlogTransaction::TYPE_STATUS:
|
case PhameBlogTransaction::TYPE_STATUS:
|
||||||
return $xaction->getNewValue();
|
return $xaction->getNewValue();
|
||||||
|
@ -65,6 +69,8 @@ final class PhameBlogEditor
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhameBlogTransaction::TYPE_NAME:
|
case PhameBlogTransaction::TYPE_NAME:
|
||||||
return $object->setName($xaction->getNewValue());
|
return $object->setName($xaction->getNewValue());
|
||||||
|
case PhameBlogTransaction::TYPE_SUBTITLE:
|
||||||
|
return $object->setSubtitle($xaction->getNewValue());
|
||||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||||
return $object->setDescription($xaction->getNewValue());
|
return $object->setDescription($xaction->getNewValue());
|
||||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||||
|
@ -82,6 +88,7 @@ final class PhameBlogEditor
|
||||||
|
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhameBlogTransaction::TYPE_NAME:
|
case PhameBlogTransaction::TYPE_NAME:
|
||||||
|
case PhameBlogTransaction::TYPE_SUBTITLE:
|
||||||
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
case PhameBlogTransaction::TYPE_DESCRIPTION:
|
||||||
case PhameBlogTransaction::TYPE_DOMAIN:
|
case PhameBlogTransaction::TYPE_DOMAIN:
|
||||||
case PhameBlogTransaction::TYPE_STATUS:
|
case PhameBlogTransaction::TYPE_STATUS:
|
||||||
|
@ -227,7 +234,7 @@ final class PhameBlogEditor
|
||||||
|
|
||||||
|
|
||||||
protected function supportsSearch() {
|
protected function supportsSearch() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function shouldApplyHeraldRules(
|
protected function shouldApplyHeraldRules(
|
||||||
|
|
|
@ -66,6 +66,9 @@ final class PhamePostEditor
|
||||||
case PhamePostTransaction::TYPE_VISIBILITY:
|
case PhamePostTransaction::TYPE_VISIBILITY:
|
||||||
if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) {
|
if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) {
|
||||||
$object->setDatePublished(0);
|
$object->setDatePublished(0);
|
||||||
|
} else if ($xaction->getNewValue() ==
|
||||||
|
PhameConstants::VISIBILITY_ARCHIVED) {
|
||||||
|
$object->setDatePublished(0);
|
||||||
} else {
|
} else {
|
||||||
$object->setDatePublished(PhabricatorTime::getNow());
|
$object->setDatePublished(PhabricatorTime::getNow());
|
||||||
}
|
}
|
||||||
|
@ -168,7 +171,7 @@ final class PhamePostEditor
|
||||||
protected function shouldSendMail(
|
protected function shouldSendMail(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
if ($object->isDraft()) {
|
if ($object->isDraft() || ($object->isArchived())) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -177,7 +180,7 @@ final class PhamePostEditor
|
||||||
protected function shouldPublishFeedStory(
|
protected function shouldPublishFeedStory(
|
||||||
PhabricatorLiskDAO $object,
|
PhabricatorLiskDAO $object,
|
||||||
array $xactions) {
|
array $xactions) {
|
||||||
if ($object->isDraft()) {
|
if ($object->isDraft() || $object->isArchived()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
|
@ -228,7 +231,7 @@ final class PhamePostEditor
|
||||||
foreach ($xactions as $xaction) {
|
foreach ($xactions as $xaction) {
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case PhamePostTransaction::TYPE_VISIBILITY:
|
case PhamePostTransaction::TYPE_VISIBILITY:
|
||||||
if (!$object->isDraft()) {
|
if (!$object->isDraft() && !$object->isArchived()) {
|
||||||
$body->addRemarkupSection(null, $object->getBody());
|
$body->addRemarkupSection(null, $object->getBody());
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
@ -261,7 +264,7 @@ final class PhamePostEditor
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function supportsSearch() {
|
protected function supportsSearch() {
|
||||||
return false;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function shouldApplyHeraldRules(
|
protected function shouldApplyHeraldRules(
|
||||||
|
|
|
@ -34,6 +34,11 @@ final class PhabricatorPhameBlogPHIDType extends PhabricatorPHIDType {
|
||||||
$handle->setName($blog->getName());
|
$handle->setName($blog->getName());
|
||||||
$handle->setFullName($blog->getName());
|
$handle->setFullName($blog->getName());
|
||||||
$handle->setURI('/phame/blog/view/'.$blog->getID().'/');
|
$handle->setURI('/phame/blog/view/'.$blog->getID().'/');
|
||||||
|
|
||||||
|
if ($blog->isArchived()) {
|
||||||
|
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -34,7 +34,13 @@ final class PhabricatorPhamePostPHIDType extends PhabricatorPHIDType {
|
||||||
$handle->setName($post->getTitle());
|
$handle->setName($post->getTitle());
|
||||||
$handle->setFullName($post->getTitle());
|
$handle->setFullName($post->getTitle());
|
||||||
$handle->setURI('/phame/post/view/'.$post->getID().'/');
|
$handle->setURI('/phame/post/view/'.$post->getID().'/');
|
||||||
|
|
||||||
|
if ($post->isArchived()) {
|
||||||
|
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
|
|
||||||
private $needBloggers;
|
private $needBloggers;
|
||||||
private $needProfileImage;
|
private $needProfileImage;
|
||||||
|
private $needHeaderImage;
|
||||||
|
|
||||||
public function withIDs(array $ids) {
|
public function withIDs(array $ids) {
|
||||||
$this->ids = $ids;
|
$this->ids = $ids;
|
||||||
|
@ -35,6 +36,11 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function needHeaderImage($need) {
|
||||||
|
$this->needHeaderImage = $need;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
public function newResultObject() {
|
public function newResultObject() {
|
||||||
return new PhameBlog();
|
return new PhameBlog();
|
||||||
}
|
}
|
||||||
|
@ -107,6 +113,28 @@ final class PhameBlogQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
$blog->attachProfileImageFile($file);
|
$blog->attachProfileImageFile($file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($this->needHeaderImage) {
|
||||||
|
$file_phids = mpull($blogs, 'getHeaderImagePHID');
|
||||||
|
$file_phids = array_filter($file_phids);
|
||||||
|
if ($file_phids) {
|
||||||
|
$files = id(new PhabricatorFileQuery())
|
||||||
|
->setParentQuery($this)
|
||||||
|
->setViewer($this->getViewer())
|
||||||
|
->withPHIDs($file_phids)
|
||||||
|
->execute();
|
||||||
|
$files = mpull($files, null, 'getPHID');
|
||||||
|
} else {
|
||||||
|
$files = array();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($blogs as $blog) {
|
||||||
|
$file = idx($files, $blog->getHeaderImagePHID());
|
||||||
|
if ($file) {
|
||||||
|
$blog->attachHeaderImageFile($file);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
return $blogs;
|
return $blogs;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function withVisibility($visibility) {
|
public function withVisibility(array $visibility) {
|
||||||
$this->visibility = $visibility;
|
$this->visibility = $visibility;
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
@ -98,10 +98,10 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
$this->bloggerPHIDs);
|
$this->bloggerPHIDs);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($this->visibility !== null) {
|
if ($this->visibility) {
|
||||||
$where[] = qsprintf(
|
$where[] = qsprintf(
|
||||||
$conn,
|
$conn,
|
||||||
'visibility = %d',
|
'visibility IN (%Ld)',
|
||||||
$this->visibility);
|
$this->visibility);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,6 +122,36 @@ final class PhamePostQuery extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||||
return $where;
|
return $where;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getBuiltinOrders() {
|
||||||
|
return array(
|
||||||
|
'datePublished' => array(
|
||||||
|
'vector' => array('datePublished', 'id'),
|
||||||
|
'name' => pht('Publish Date'),
|
||||||
|
),
|
||||||
|
) + parent::getBuiltinOrders();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getOrderableColumns() {
|
||||||
|
return parent::getOrderableColumns() + array(
|
||||||
|
'datePublished' => array(
|
||||||
|
'column' => 'datePublished',
|
||||||
|
'type' => 'int',
|
||||||
|
'reverse' => false,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function getPagingValueMap($cursor, array $keys) {
|
||||||
|
$post = $this->loadCursorObject($cursor);
|
||||||
|
|
||||||
|
$map = array(
|
||||||
|
'datePublished' => $post->getDatePublished(),
|
||||||
|
'id' => $post->getID(),
|
||||||
|
);
|
||||||
|
|
||||||
|
return $map;
|
||||||
|
}
|
||||||
|
|
||||||
public function getQueryApplicationClass() {
|
public function getQueryApplicationClass() {
|
||||||
// TODO: Does setting this break public blogs?
|
// TODO: Does setting this break public blogs?
|
||||||
return null;
|
return null;
|
||||||
|
|
|
@ -19,7 +19,7 @@ final class PhamePostSearchEngine
|
||||||
$query = $this->newQuery();
|
$query = $this->newQuery();
|
||||||
|
|
||||||
if (strlen($map['visibility'])) {
|
if (strlen($map['visibility'])) {
|
||||||
$query->withVisibility($map['visibility']);
|
$query->withVisibility(array($map['visibility']));
|
||||||
}
|
}
|
||||||
|
|
||||||
return $query;
|
return $query;
|
||||||
|
@ -35,6 +35,7 @@ final class PhamePostSearchEngine
|
||||||
'' => pht('All'),
|
'' => pht('All'),
|
||||||
PhameConstants::VISIBILITY_PUBLISHED => pht('Published'),
|
PhameConstants::VISIBILITY_PUBLISHED => pht('Published'),
|
||||||
PhameConstants::VISIBILITY_DRAFT => pht('Draft'),
|
PhameConstants::VISIBILITY_DRAFT => pht('Draft'),
|
||||||
|
PhameConstants::VISIBILITY_ARCHIVED => pht('Archived'),
|
||||||
)),
|
)),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -48,6 +49,7 @@ final class PhamePostSearchEngine
|
||||||
'all' => pht('All Posts'),
|
'all' => pht('All Posts'),
|
||||||
'live' => pht('Published Posts'),
|
'live' => pht('Published Posts'),
|
||||||
'draft' => pht('Draft Posts'),
|
'draft' => pht('Draft Posts'),
|
||||||
|
'archived' => pht('Archived Posts'),
|
||||||
);
|
);
|
||||||
return $names;
|
return $names;
|
||||||
}
|
}
|
||||||
|
@ -65,6 +67,9 @@ final class PhamePostSearchEngine
|
||||||
case 'draft':
|
case 'draft':
|
||||||
return $query->setParameter(
|
return $query->setParameter(
|
||||||
'visibility', PhameConstants::VISIBILITY_DRAFT);
|
'visibility', PhameConstants::VISIBILITY_DRAFT);
|
||||||
|
case 'archived':
|
||||||
|
return $query->setParameter(
|
||||||
|
'visibility', PhameConstants::VISIBILITY_ARCHIVED);
|
||||||
}
|
}
|
||||||
|
|
||||||
return parent::buildSavedQueryFromBuiltin($query_key);
|
return parent::buildSavedQueryFromBuiltin($query_key);
|
||||||
|
@ -99,11 +104,19 @@ final class PhamePostSearchEngine
|
||||||
if ($post->isDraft()) {
|
if ($post->isDraft()) {
|
||||||
$item->setStatusIcon('fa-star-o grey');
|
$item->setStatusIcon('fa-star-o grey');
|
||||||
$item->setDisabled(true);
|
$item->setDisabled(true);
|
||||||
$item->addIcon('none', pht('Draft Post'));
|
$item->addIcon('fa-star-o', pht('Draft Post'));
|
||||||
|
} else if ($post->isArchived()) {
|
||||||
|
$item->setStatusIcon('fa-ban grey');
|
||||||
|
$item->setDisabled(true);
|
||||||
|
$item->addIcon('fa-ban', pht('Archived Post'));
|
||||||
} else {
|
} else {
|
||||||
$date = $post->getDatePublished();
|
$date = $post->getDatePublished();
|
||||||
$item->setEpoch($date);
|
$item->setEpoch($date);
|
||||||
}
|
}
|
||||||
|
$item->addAction(
|
||||||
|
id(new PHUIListItemView())
|
||||||
|
->setIcon('fa-pencil')
|
||||||
|
->setHref($post->getEditURI()));
|
||||||
$list->addItem($item);
|
$list->addItem($item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
28
src/applications/phame/search/PhameBlogFulltextEngine.php
Normal file
28
src/applications/phame/search/PhameBlogFulltextEngine.php
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhameBlogFulltextEngine
|
||||||
|
extends PhabricatorFulltextEngine {
|
||||||
|
|
||||||
|
protected function buildAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document,
|
||||||
|
$object) {
|
||||||
|
|
||||||
|
$blog = $object;
|
||||||
|
|
||||||
|
$document->setDocumentTitle($blog->getName());
|
||||||
|
|
||||||
|
$document->addField(
|
||||||
|
PhabricatorSearchDocumentFieldType::FIELD_BODY,
|
||||||
|
$blog->getDescription());
|
||||||
|
|
||||||
|
$document->addRelationship(
|
||||||
|
$blog->isArchived()
|
||||||
|
? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
|
||||||
|
: PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
|
||||||
|
$blog->getPHID(),
|
||||||
|
PhabricatorPhameBlogPHIDType::TYPECONST,
|
||||||
|
PhabricatorTime::getNow());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
34
src/applications/phame/search/PhamePostFulltextEngine.php
Normal file
34
src/applications/phame/search/PhamePostFulltextEngine.php
Normal file
|
@ -0,0 +1,34 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhamePostFulltextEngine
|
||||||
|
extends PhabricatorFulltextEngine {
|
||||||
|
|
||||||
|
protected function buildAbstractDocument(
|
||||||
|
PhabricatorSearchAbstractDocument $document,
|
||||||
|
$object) {
|
||||||
|
|
||||||
|
$post = $object;
|
||||||
|
|
||||||
|
$document->setDocumentTitle($post->getTitle());
|
||||||
|
|
||||||
|
$document->addField(
|
||||||
|
PhabricatorSearchDocumentFieldType::FIELD_BODY,
|
||||||
|
$post->getBody());
|
||||||
|
|
||||||
|
$document->addRelationship(
|
||||||
|
PhabricatorSearchRelationship::RELATIONSHIP_AUTHOR,
|
||||||
|
$post->getBloggerPHID(),
|
||||||
|
PhabricatorPeopleUserPHIDType::TYPECONST,
|
||||||
|
$post->getDateCreated());
|
||||||
|
|
||||||
|
$document->addRelationship(
|
||||||
|
$post->isArchived()
|
||||||
|
? PhabricatorSearchRelationship::RELATIONSHIP_CLOSED
|
||||||
|
: PhabricatorSearchRelationship::RELATIONSHIP_OPEN,
|
||||||
|
$post->getPHID(),
|
||||||
|
PhabricatorPhamePostPHIDType::TYPECONST,
|
||||||
|
PhabricatorTime::getNow());
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -9,11 +9,13 @@ final class PhameBlog extends PhameDAO
|
||||||
PhabricatorProjectInterface,
|
PhabricatorProjectInterface,
|
||||||
PhabricatorDestructibleInterface,
|
PhabricatorDestructibleInterface,
|
||||||
PhabricatorApplicationTransactionInterface,
|
PhabricatorApplicationTransactionInterface,
|
||||||
PhabricatorConduitResultInterface {
|
PhabricatorConduitResultInterface,
|
||||||
|
PhabricatorFulltextInterface {
|
||||||
|
|
||||||
const MARKUP_FIELD_DESCRIPTION = 'markup:description';
|
const MARKUP_FIELD_DESCRIPTION = 'markup:description';
|
||||||
|
|
||||||
protected $name;
|
protected $name;
|
||||||
|
protected $subtitle;
|
||||||
protected $description;
|
protected $description;
|
||||||
protected $domain;
|
protected $domain;
|
||||||
protected $configData;
|
protected $configData;
|
||||||
|
@ -23,8 +25,10 @@ final class PhameBlog extends PhameDAO
|
||||||
protected $status;
|
protected $status;
|
||||||
protected $mailKey;
|
protected $mailKey;
|
||||||
protected $profileImagePHID;
|
protected $profileImagePHID;
|
||||||
|
protected $headerImagePHID;
|
||||||
|
|
||||||
private $profileImageFile = self::ATTACHABLE;
|
private $profileImageFile = self::ATTACHABLE;
|
||||||
|
private $headerImageFile = self::ATTACHABLE;
|
||||||
|
|
||||||
const STATUS_ACTIVE = 'active';
|
const STATUS_ACTIVE = 'active';
|
||||||
const STATUS_ARCHIVED = 'archived';
|
const STATUS_ARCHIVED = 'archived';
|
||||||
|
@ -37,11 +41,13 @@ final class PhameBlog extends PhameDAO
|
||||||
),
|
),
|
||||||
self::CONFIG_COLUMN_SCHEMA => array(
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
'name' => 'text64',
|
'name' => 'text64',
|
||||||
|
'subtitle' => 'text64',
|
||||||
'description' => 'text',
|
'description' => 'text',
|
||||||
'domain' => 'text128?',
|
'domain' => 'text128?',
|
||||||
'status' => 'text32',
|
'status' => 'text32',
|
||||||
'mailKey' => 'bytes20',
|
'mailKey' => 'bytes20',
|
||||||
'profileImagePHID' => 'phid?',
|
'profileImagePHID' => 'phid?',
|
||||||
|
'headerImagePHID' => 'phid?',
|
||||||
|
|
||||||
// T6203/NULLABILITY
|
// T6203/NULLABILITY
|
||||||
// These policies should always be non-null.
|
// These policies should always be non-null.
|
||||||
|
@ -211,6 +217,19 @@ final class PhameBlog extends PhameDAO
|
||||||
return $this->assertAttached($this->profileImageFile);
|
return $this->assertAttached($this->profileImageFile);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function getHeaderImageURI() {
|
||||||
|
return $this->getHeaderImageFile()->getBestURI();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function attachHeaderImageFile(PhabricatorFile $file) {
|
||||||
|
$this->headerImageFile = $file;
|
||||||
|
return $this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getHeaderImageFile() {
|
||||||
|
return $this->assertAttached($this->headerImageFile);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
||||||
|
|
||||||
|
@ -370,4 +389,10 @@ final class PhameBlog extends PhameDAO
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorFulltextInterface )--------------------------------------- */
|
||||||
|
|
||||||
|
public function newFulltextEngine() {
|
||||||
|
return new PhameBlogFulltextEngine();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,6 +4,7 @@ final class PhameBlogTransaction
|
||||||
extends PhabricatorApplicationTransaction {
|
extends PhabricatorApplicationTransaction {
|
||||||
|
|
||||||
const TYPE_NAME = 'phame.blog.name';
|
const TYPE_NAME = 'phame.blog.name';
|
||||||
|
const TYPE_SUBTITLE = 'phame.blog.subtitle';
|
||||||
const TYPE_DESCRIPTION = 'phame.blog.description';
|
const TYPE_DESCRIPTION = 'phame.blog.description';
|
||||||
const TYPE_DOMAIN = 'phame.blog.domain';
|
const TYPE_DOMAIN = 'phame.blog.domain';
|
||||||
const TYPE_STATUS = 'phame.blog.status';
|
const TYPE_STATUS = 'phame.blog.status';
|
||||||
|
@ -80,6 +81,7 @@ final class PhameBlogTransaction
|
||||||
$tags[] = self::MAILTAG_SUBSCRIBERS;
|
$tags[] = self::MAILTAG_SUBSCRIBERS;
|
||||||
break;
|
break;
|
||||||
case self::TYPE_NAME:
|
case self::TYPE_NAME:
|
||||||
|
case self::TYPE_SUBTITLE:
|
||||||
case self::TYPE_DESCRIPTION:
|
case self::TYPE_DESCRIPTION:
|
||||||
case self::TYPE_DOMAIN:
|
case self::TYPE_DOMAIN:
|
||||||
$tags[] = self::MAILTAG_DETAILS;
|
$tags[] = self::MAILTAG_DETAILS;
|
||||||
|
@ -116,6 +118,19 @@ final class PhameBlogTransaction
|
||||||
$new);
|
$new);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_SUBTITLE:
|
||||||
|
if ($old === null) {
|
||||||
|
return pht(
|
||||||
|
'%s set this blog\'s subtitle to "%s".',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$new);
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s updated the blog\'s subtitle to "%s".',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$new);
|
||||||
|
}
|
||||||
|
break;
|
||||||
case self::TYPE_DESCRIPTION:
|
case self::TYPE_DESCRIPTION:
|
||||||
return pht(
|
return pht(
|
||||||
'%s updated the blog\'s description.',
|
'%s updated the blog\'s description.',
|
||||||
|
@ -166,6 +181,19 @@ final class PhameBlogTransaction
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case self::TYPE_SUBTITLE:
|
||||||
|
if ($old === null) {
|
||||||
|
return pht(
|
||||||
|
'%s set the subtitle for %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
|
} else {
|
||||||
|
return pht(
|
||||||
|
'%s updated the subtitle for %s.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
|
}
|
||||||
|
break;
|
||||||
case self::TYPE_DESCRIPTION:
|
case self::TYPE_DESCRIPTION:
|
||||||
return pht(
|
return pht(
|
||||||
'%s updated the description for %s.',
|
'%s updated the description for %s.',
|
||||||
|
|
|
@ -10,7 +10,8 @@ final class PhamePost extends PhameDAO
|
||||||
PhabricatorSubscribableInterface,
|
PhabricatorSubscribableInterface,
|
||||||
PhabricatorDestructibleInterface,
|
PhabricatorDestructibleInterface,
|
||||||
PhabricatorTokenReceiverInterface,
|
PhabricatorTokenReceiverInterface,
|
||||||
PhabricatorConduitResultInterface {
|
PhabricatorConduitResultInterface,
|
||||||
|
PhabricatorFulltextInterface {
|
||||||
|
|
||||||
const MARKUP_FIELD_BODY = 'markup:body';
|
const MARKUP_FIELD_BODY = 'markup:body';
|
||||||
const MARKUP_FIELD_SUMMARY = 'markup:summary';
|
const MARKUP_FIELD_SUMMARY = 'markup:summary';
|
||||||
|
@ -53,7 +54,8 @@ final class PhamePost extends PhameDAO
|
||||||
public function getLiveURI() {
|
public function getLiveURI() {
|
||||||
$blog = $this->getBlog();
|
$blog = $this->getBlog();
|
||||||
$is_draft = $this->isDraft();
|
$is_draft = $this->isDraft();
|
||||||
if (strlen($blog->getDomain()) && !$is_draft) {
|
$is_archived = $this->isArchived();
|
||||||
|
if (strlen($blog->getDomain()) && !$is_draft && !$is_archived) {
|
||||||
return $this->getExternalLiveURI();
|
return $this->getExternalLiveURI();
|
||||||
} else {
|
} else {
|
||||||
return $this->getInternalLiveURI();
|
return $this->getInternalLiveURI();
|
||||||
|
@ -92,6 +94,10 @@ final class PhamePost extends PhameDAO
|
||||||
return ($this->getVisibility() == PhameConstants::VISIBILITY_DRAFT);
|
return ($this->getVisibility() == PhameConstants::VISIBILITY_DRAFT);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isArchived() {
|
||||||
|
return ($this->getVisibility() == PhameConstants::VISIBILITY_ARCHIVED);
|
||||||
|
}
|
||||||
|
|
||||||
protected function getConfiguration() {
|
protected function getConfiguration() {
|
||||||
return array(
|
return array(
|
||||||
self::CONFIG_AUX_PHID => true,
|
self::CONFIG_AUX_PHID => true,
|
||||||
|
@ -165,7 +171,7 @@ final class PhamePost extends PhameDAO
|
||||||
|
|
||||||
switch ($capability) {
|
switch ($capability) {
|
||||||
case PhabricatorPolicyCapability::CAN_VIEW:
|
case PhabricatorPolicyCapability::CAN_VIEW:
|
||||||
if (!$this->isDraft() && $this->getBlog()) {
|
if (!$this->isDraft() && !$this->isArchived() && $this->getBlog()) {
|
||||||
return $this->getBlog()->getViewPolicy();
|
return $this->getBlog()->getViewPolicy();
|
||||||
} else if ($this->getBlog()) {
|
} else if ($this->getBlog()) {
|
||||||
return $this->getBlog()->getEditPolicy();
|
return $this->getBlog()->getEditPolicy();
|
||||||
|
@ -319,6 +325,8 @@ final class PhamePost extends PhameDAO
|
||||||
public function getFieldValuesForConduit() {
|
public function getFieldValuesForConduit() {
|
||||||
if ($this->isDraft()) {
|
if ($this->isDraft()) {
|
||||||
$date_published = null;
|
$date_published = null;
|
||||||
|
} else if ($this->isArchived()) {
|
||||||
|
$date_published = null;
|
||||||
} else {
|
} else {
|
||||||
$date_published = (int)$this->getDatePublished();
|
$date_published = (int)$this->getDatePublished();
|
||||||
}
|
}
|
||||||
|
@ -337,4 +345,11 @@ final class PhamePost extends PhameDAO
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorFulltextInterface )--------------------------------------- */
|
||||||
|
|
||||||
|
public function newFulltextEngine() {
|
||||||
|
return new PhamePostFulltextEngine();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -73,6 +73,8 @@ final class PhamePostTransaction
|
||||||
case self::TYPE_VISIBILITY:
|
case self::TYPE_VISIBILITY:
|
||||||
if ($new == PhameConstants::VISIBILITY_PUBLISHED) {
|
if ($new == PhameConstants::VISIBILITY_PUBLISHED) {
|
||||||
return 'fa-globe';
|
return 'fa-globe';
|
||||||
|
} else if ($new == PhameConstants::VISIBILITY_ARCHIVED) {
|
||||||
|
return 'fa-ban';
|
||||||
} else {
|
} else {
|
||||||
return 'fa-eye-slash';
|
return 'fa-eye-slash';
|
||||||
}
|
}
|
||||||
|
@ -144,6 +146,10 @@ final class PhamePostTransaction
|
||||||
return pht(
|
return pht(
|
||||||
'%s marked this post as a draft.',
|
'%s marked this post as a draft.',
|
||||||
$this->renderHandleLink($author_phid));
|
$this->renderHandleLink($author_phid));
|
||||||
|
} else if ($new == PhameConstants::VISIBILITY_ARCHIVED) {
|
||||||
|
return pht(
|
||||||
|
'%s archived this post.',
|
||||||
|
$this->renderHandleLink($author_phid));
|
||||||
} else {
|
} else {
|
||||||
return pht(
|
return pht(
|
||||||
'%s published this post.',
|
'%s published this post.',
|
||||||
|
@ -201,6 +207,11 @@ final class PhamePostTransaction
|
||||||
'%s marked %s as a draft.',
|
'%s marked %s as a draft.',
|
||||||
$this->renderHandleLink($author_phid),
|
$this->renderHandleLink($author_phid),
|
||||||
$this->renderHandleLink($object_phid));
|
$this->renderHandleLink($object_phid));
|
||||||
|
} else if ($new == PhameConstants::VISIBILITY_ARCHIVED) {
|
||||||
|
return pht(
|
||||||
|
'%s marked %s as archived.',
|
||||||
|
$this->renderHandleLink($author_phid),
|
||||||
|
$this->renderHandleLink($object_phid));
|
||||||
} else {
|
} else {
|
||||||
return pht(
|
return pht(
|
||||||
'%s published %s.',
|
'%s published %s.',
|
||||||
|
|
|
@ -1099,6 +1099,18 @@ final class PhabricatorProjectCoreTestCase extends PhabricatorTestCase {
|
||||||
$column = $this->refreshColumn($user, $column);
|
$column = $this->refreshColumn($user, $column);
|
||||||
$this->assertTrue((bool)$column);
|
$this->assertTrue((bool)$column);
|
||||||
|
|
||||||
|
// This test has been failing randomly in a way that doesn't reproduce
|
||||||
|
// on any host, so add some extra assertions to try to nail it down.
|
||||||
|
$board = $this->refreshProject($board, $user, true);
|
||||||
|
$this->assertTrue((bool)$board);
|
||||||
|
$this->assertTrue($board->isUserMember($user->getPHID()));
|
||||||
|
|
||||||
|
$can_view = PhabricatorPolicyFilter::hasCapability(
|
||||||
|
$user,
|
||||||
|
$column,
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW);
|
||||||
|
$this->assertTrue($can_view);
|
||||||
|
|
||||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||||
$user,
|
$user,
|
||||||
$column,
|
$column,
|
||||||
|
|
|
@ -11,14 +11,20 @@ final class PhabricatorGitGraphStream
|
||||||
|
|
||||||
public function __construct(
|
public function __construct(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
$start_commit) {
|
$start_commit = null) {
|
||||||
|
|
||||||
$this->repository = $repository;
|
$this->repository = $repository;
|
||||||
|
|
||||||
$future = $repository->getLocalCommandFuture(
|
if ($start_commit !== null) {
|
||||||
'log --format=%s %s --',
|
$future = $repository->getLocalCommandFuture(
|
||||||
'%H%x01%P%x01%ct',
|
'log --format=%s %s --',
|
||||||
$start_commit);
|
'%H%x01%P%x01%ct',
|
||||||
|
$start_commit);
|
||||||
|
} else {
|
||||||
|
$future = $repository->getLocalCommandFuture(
|
||||||
|
'log --format=%s --all --',
|
||||||
|
'%H%x01%P%x01%ct');
|
||||||
|
}
|
||||||
|
|
||||||
$this->iterator = new LinesOfALargeExecFuture($future);
|
$this->iterator = new LinesOfALargeExecFuture($future);
|
||||||
$this->iterator->setDelimiter("\n");
|
$this->iterator->setDelimiter("\n");
|
||||||
|
|
|
@ -105,6 +105,8 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
$this->commitCache[$ref->getIdentifier()] = true;
|
$this->commitCache[$ref->getIdentifier()] = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->markUnreachableCommits($repository);
|
||||||
|
|
||||||
$version = $this->getObservedVersion($repository);
|
$version = $this->getObservedVersion($repository);
|
||||||
if ($version !== null) {
|
if ($version !== null) {
|
||||||
id(new DiffusionRepositoryClusterEngine())
|
id(new DiffusionRepositoryClusterEngine())
|
||||||
|
@ -130,39 +132,35 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
$this->verifyGitOrigin($repository);
|
$this->verifyGitOrigin($repository);
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: This should also import tags, but some of the logic is still
|
$heads = id(new DiffusionLowLevelGitRefQuery())
|
||||||
// branch-specific today.
|
|
||||||
|
|
||||||
$branches = id(new DiffusionLowLevelGitRefQuery())
|
|
||||||
->setRepository($repository)
|
->setRepository($repository)
|
||||||
->withRefTypes(
|
|
||||||
array(
|
|
||||||
PhabricatorRepositoryRefCursor::TYPE_BRANCH,
|
|
||||||
))
|
|
||||||
->execute();
|
->execute();
|
||||||
|
|
||||||
if (!$branches) {
|
if (!$heads) {
|
||||||
// This repository has no branches at all, so we don't need to do
|
// This repository has no heads at all, so we don't need to do
|
||||||
// anything. Generally, this means the repository is empty.
|
// anything. Generally, this means the repository is empty.
|
||||||
return array();
|
return array();
|
||||||
}
|
}
|
||||||
|
|
||||||
$branches = $this->sortBranches($branches);
|
$heads = $this->sortRefs($heads);
|
||||||
$branches = mpull($branches, 'getCommitIdentifier', 'getShortName');
|
$head_commits = mpull($heads, 'getCommitIdentifier');
|
||||||
|
|
||||||
$this->log(
|
$this->log(
|
||||||
pht(
|
pht(
|
||||||
'Discovering commits in repository "%s".',
|
'Discovering commits in repository "%s".',
|
||||||
$repository->getDisplayName()));
|
$repository->getDisplayName()));
|
||||||
|
|
||||||
$this->fillCommitCache(array_values($branches));
|
$this->fillCommitCache($head_commits);
|
||||||
|
|
||||||
$refs = array();
|
$refs = array();
|
||||||
foreach ($branches as $name => $commit) {
|
foreach ($heads as $ref) {
|
||||||
$this->log(pht('Examining branch "%s", at "%s".', $name, $commit));
|
$name = $ref->getShortName();
|
||||||
|
$commit = $ref->getCommitIdentifier();
|
||||||
|
|
||||||
if (!$repository->shouldTrackBranch($name)) {
|
$this->log(pht('Examining ref "%s", at "%s".', $name, $commit));
|
||||||
$this->log(pht('Skipping, branch is untracked.'));
|
|
||||||
|
if (!$repository->shouldTrackRef($ref)) {
|
||||||
|
$this->log(pht('Skipping, ref is untracked.'));
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -173,14 +171,14 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
|
|
||||||
$this->log(pht('Looking for new commits.'));
|
$this->log(pht('Looking for new commits.'));
|
||||||
|
|
||||||
$branch_refs = $this->discoverStreamAncestry(
|
$head_refs = $this->discoverStreamAncestry(
|
||||||
new PhabricatorGitGraphStream($repository, $commit),
|
new PhabricatorGitGraphStream($repository, $commit),
|
||||||
$commit,
|
$commit,
|
||||||
$repository->shouldAutocloseBranch($name));
|
$repository->shouldAutocloseRef($ref));
|
||||||
|
|
||||||
$this->didDiscoverRefs($branch_refs);
|
$this->didDiscoverRefs($head_refs);
|
||||||
|
|
||||||
$refs[] = $branch_refs;
|
$refs[] = $head_refs;
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_mergev($refs);
|
return array_mergev($refs);
|
||||||
|
@ -448,10 +446,17 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// When filling the cache we ignore commits which have been marked as
|
||||||
|
// unreachable, treating them as though they do not exist. When recording
|
||||||
|
// commits later we'll revive commits that exist but are unreachable.
|
||||||
|
|
||||||
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
|
$commits = id(new PhabricatorRepositoryCommit())->loadAllWhere(
|
||||||
'repositoryID = %d AND commitIdentifier IN (%Ls)',
|
'repositoryID = %d AND commitIdentifier IN (%Ls)
|
||||||
|
AND (importStatus & %d) != %d',
|
||||||
$this->getRepository()->getID(),
|
$this->getRepository()->getID(),
|
||||||
$identifiers);
|
$identifiers,
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
|
||||||
|
|
||||||
foreach ($commits as $commit) {
|
foreach ($commits as $commit) {
|
||||||
$this->commitCache[$commit->getCommitIdentifier()] = true;
|
$this->commitCache[$commit->getCommitIdentifier()] = true;
|
||||||
|
@ -469,25 +474,23 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
*
|
*
|
||||||
* @task internal
|
* @task internal
|
||||||
*
|
*
|
||||||
* @param list<DiffusionRepositoryRef> List of branch heads.
|
* @param list<DiffusionRepositoryRef> List of refs.
|
||||||
* @return list<DiffusionRepositoryRef> Sorted list of branch heads.
|
* @return list<DiffusionRepositoryRef> Sorted list of refs.
|
||||||
*/
|
*/
|
||||||
private function sortBranches(array $branches) {
|
private function sortRefs(array $refs) {
|
||||||
$repository = $this->getRepository();
|
$repository = $this->getRepository();
|
||||||
|
|
||||||
$head_branches = array();
|
$head_refs = array();
|
||||||
$tail_branches = array();
|
$tail_refs = array();
|
||||||
foreach ($branches as $branch) {
|
foreach ($refs as $ref) {
|
||||||
$name = $branch->getShortName();
|
if ($repository->shouldAutocloseRef($ref)) {
|
||||||
|
$head_refs[] = $ref;
|
||||||
if ($repository->shouldAutocloseBranch($name)) {
|
|
||||||
$head_branches[] = $branch;
|
|
||||||
} else {
|
} else {
|
||||||
$tail_branches[] = $branch;
|
$tail_refs[] = $ref;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return array_merge($head_branches, $tail_branches);
|
return array_merge($head_refs, $tail_refs);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -499,6 +502,30 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
array $parents) {
|
array $parents) {
|
||||||
|
|
||||||
$commit = new PhabricatorRepositoryCommit();
|
$commit = new PhabricatorRepositoryCommit();
|
||||||
|
$conn_w = $repository->establishConnection('w');
|
||||||
|
|
||||||
|
// First, try to revive an existing unreachable commit (if one exists) by
|
||||||
|
// removing the "unreachable" flag. If we succeed, we don't need to do
|
||||||
|
// anything else: we already discovered this commit some time ago.
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET importStatus = (importStatus & ~%d)
|
||||||
|
WHERE repositoryID = %d AND commitIdentifier = %s',
|
||||||
|
$commit->getTableName(),
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
|
||||||
|
$repository->getID(),
|
||||||
|
$commit_identifier);
|
||||||
|
if ($conn_w->getAffectedRows()) {
|
||||||
|
$commit = $commit->loadOneWhere(
|
||||||
|
'repositoryID = %d AND commitIdentifier = %s',
|
||||||
|
$repository->getID(),
|
||||||
|
$commit_identifier);
|
||||||
|
|
||||||
|
// After reviving a commit, schedule new daemons for it.
|
||||||
|
$this->didDiscoverCommit($repository, $commit, $epoch);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
$commit->setRepositoryID($repository->getID());
|
$commit->setRepositoryID($repository->getID());
|
||||||
$commit->setCommitIdentifier($commit_identifier);
|
$commit->setCommitIdentifier($commit_identifier);
|
||||||
$commit->setEpoch($epoch);
|
$commit->setEpoch($epoch);
|
||||||
|
@ -508,10 +535,7 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
|
|
||||||
$data = new PhabricatorRepositoryCommitData();
|
$data = new PhabricatorRepositoryCommitData();
|
||||||
|
|
||||||
$conn_w = $repository->establishConnection('w');
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// If this commit has parents, look up their IDs. The parent commits
|
// If this commit has parents, look up their IDs. The parent commits
|
||||||
// should always exist already.
|
// should always exist already.
|
||||||
|
|
||||||
|
@ -559,21 +583,7 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
}
|
}
|
||||||
$commit->saveTransaction();
|
$commit->saveTransaction();
|
||||||
|
|
||||||
$this->insertTask($repository, $commit);
|
$this->didDiscoverCommit($repository, $commit, $epoch);
|
||||||
|
|
||||||
queryfx(
|
|
||||||
$conn_w,
|
|
||||||
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
|
|
||||||
VALUES (%d, 1, %d, %d)
|
|
||||||
ON DUPLICATE KEY UPDATE
|
|
||||||
size = size + 1,
|
|
||||||
lastCommitID =
|
|
||||||
IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
|
|
||||||
epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
|
|
||||||
PhabricatorRepository::TABLE_SUMMARY,
|
|
||||||
$repository->getID(),
|
|
||||||
$commit->getID(),
|
|
||||||
$epoch);
|
|
||||||
|
|
||||||
if ($this->repairMode) {
|
if ($this->repairMode) {
|
||||||
// Normally, the query should throw a duplicate key exception. If we
|
// Normally, the query should throw a duplicate key exception. If we
|
||||||
|
@ -589,8 +599,6 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
'commit' => $commit,
|
'commit' => $commit,
|
||||||
)));
|
)));
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
} catch (AphrontDuplicateKeyQueryException $ex) {
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||||
$commit->killTransaction();
|
$commit->killTransaction();
|
||||||
// Ignore. This can happen because we discover the same new commit
|
// Ignore. This can happen because we discover the same new commit
|
||||||
|
@ -600,6 +608,29 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function didDiscoverCommit(
|
||||||
|
PhabricatorRepository $repository,
|
||||||
|
PhabricatorRepositoryCommit $commit,
|
||||||
|
$epoch) {
|
||||||
|
|
||||||
|
$this->insertTask($repository, $commit);
|
||||||
|
|
||||||
|
// Update the repository summary table.
|
||||||
|
queryfx(
|
||||||
|
$commit->establishConnection('w'),
|
||||||
|
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
|
||||||
|
VALUES (%d, 1, %d, %d)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
size = size + 1,
|
||||||
|
lastCommitID =
|
||||||
|
IF(VALUES(epoch) > epoch, VALUES(lastCommitID), lastCommitID),
|
||||||
|
epoch = IF(VALUES(epoch) > epoch, VALUES(epoch), epoch)',
|
||||||
|
PhabricatorRepository::TABLE_SUMMARY,
|
||||||
|
$repository->getID(),
|
||||||
|
$commit->getID(),
|
||||||
|
$epoch);
|
||||||
|
}
|
||||||
|
|
||||||
private function didDiscoverRefs(array $refs) {
|
private function didDiscoverRefs(array $refs) {
|
||||||
foreach ($refs as $ref) {
|
foreach ($refs as $ref) {
|
||||||
$this->workingSet[$ref->getIdentifier()] = true;
|
$this->workingSet[$ref->getIdentifier()] = true;
|
||||||
|
@ -702,4 +733,136 @@ final class PhabricatorRepositoryDiscoveryEngine
|
||||||
return (int)$version['version'];
|
return (int)$version['version'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private function markUnreachableCommits(PhabricatorRepository $repository) {
|
||||||
|
// For now, this is only supported for Git.
|
||||||
|
if (!$repository->isGit()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Find older versions of refs which we haven't processed yet. We're going
|
||||||
|
// to make sure their commits are still reachable.
|
||||||
|
$old_refs = id(new PhabricatorRepositoryOldRef())->loadAllWhere(
|
||||||
|
'repositoryPHID = %s',
|
||||||
|
$repository->getPHID());
|
||||||
|
|
||||||
|
// We can share a single graph stream across all the checks we need to do.
|
||||||
|
$stream = new PhabricatorGitGraphStream($repository);
|
||||||
|
|
||||||
|
foreach ($old_refs as $old_ref) {
|
||||||
|
$identifier = $old_ref->getCommitIdentifier();
|
||||||
|
$this->markUnreachableFrom($repository, $stream, $identifier);
|
||||||
|
|
||||||
|
// If nothing threw an exception, we're all done with this ref.
|
||||||
|
$old_ref->delete();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function markUnreachableFrom(
|
||||||
|
PhabricatorRepository $repository,
|
||||||
|
PhabricatorGitGraphStream $stream,
|
||||||
|
$identifier) {
|
||||||
|
|
||||||
|
$unreachable = array();
|
||||||
|
|
||||||
|
$commit = id(new PhabricatorRepositoryCommit())->loadOneWhere(
|
||||||
|
'repositoryID = %s AND commitIdentifier = %s',
|
||||||
|
$repository->getID(),
|
||||||
|
$identifier);
|
||||||
|
if (!$commit) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
$look = array($commit);
|
||||||
|
$seen = array();
|
||||||
|
while ($look) {
|
||||||
|
$target = array_pop($look);
|
||||||
|
|
||||||
|
// If we've already checked this commit (for example, because history
|
||||||
|
// branches and then merges) we don't need to check it again.
|
||||||
|
$target_identifier = $target->getCommitIdentifier();
|
||||||
|
if (isset($seen[$target_identifier])) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$seen[$target_identifier] = true;
|
||||||
|
|
||||||
|
try {
|
||||||
|
$stream->getCommitDate($target_identifier);
|
||||||
|
$reachable = true;
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$reachable = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($reachable) {
|
||||||
|
// This commit is reachable, so we don't need to go any further
|
||||||
|
// down this road.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$unreachable[] = $target;
|
||||||
|
|
||||||
|
// Find the commit's parents and check them for reachability, too. We
|
||||||
|
// have to look in the database since we no may longer have the commit
|
||||||
|
// in the repository.
|
||||||
|
$rows = queryfx_all(
|
||||||
|
$commit->establishConnection('w'),
|
||||||
|
'SELECT commit.* FROM %T commit
|
||||||
|
JOIN %T parents ON commit.id = parents.parentCommitID
|
||||||
|
WHERE parents.childCommitID = %d',
|
||||||
|
$commit->getTableName(),
|
||||||
|
PhabricatorRepository::TABLE_PARENTS,
|
||||||
|
$target->getID());
|
||||||
|
if (!$rows) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
$parents = id(new PhabricatorRepositoryCommit())
|
||||||
|
->loadAllFromArray($rows);
|
||||||
|
foreach ($parents as $parent) {
|
||||||
|
$look[] = $parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$unreachable = array_reverse($unreachable);
|
||||||
|
|
||||||
|
$flag = PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE;
|
||||||
|
foreach ($unreachable as $unreachable_commit) {
|
||||||
|
$unreachable_commit->writeImportStatusFlag($flag);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If anything was unreachable, just rebuild the whole summary table.
|
||||||
|
// We can't really update it incrementally when a commit becomes
|
||||||
|
// unreachable.
|
||||||
|
if ($unreachable) {
|
||||||
|
$this->rebuildSummaryTable($repository);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private function rebuildSummaryTable(PhabricatorRepository $repository) {
|
||||||
|
$conn_w = $repository->establishConnection('w');
|
||||||
|
|
||||||
|
$data = queryfx_one(
|
||||||
|
$conn_w,
|
||||||
|
'SELECT COUNT(*) N, MAX(id) id, MAX(epoch) epoch
|
||||||
|
FROM %T WHERE repositoryID = %d AND (importStatus & %d) != %d',
|
||||||
|
id(new PhabricatorRepositoryCommit())->getTableName(),
|
||||||
|
$repository->getID(),
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE,
|
||||||
|
PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE);
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'INSERT INTO %T (repositoryID, size, lastCommitID, epoch)
|
||||||
|
VALUES (%d, %d, %d, %d)
|
||||||
|
ON DUPLICATE KEY UPDATE
|
||||||
|
size = VALUES(size),
|
||||||
|
lastCommitID = VALUES(lastCommitID),
|
||||||
|
epoch = VALUES(epoch)',
|
||||||
|
PhabricatorRepository::TABLE_SUMMARY,
|
||||||
|
$repository->getID(),
|
||||||
|
$data['N'],
|
||||||
|
$data['id'],
|
||||||
|
$data['epoch']);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -347,7 +347,7 @@ final class PhabricatorRepositoryPullEngine
|
||||||
// For bare working copies, we need this magic incantation.
|
// For bare working copies, we need this magic incantation.
|
||||||
$future = $repository->getRemoteCommandFuture(
|
$future = $repository->getRemoteCommandFuture(
|
||||||
'fetch origin %s --prune',
|
'fetch origin %s --prune',
|
||||||
'+refs/heads/*:refs/heads/*');
|
'+refs/*:refs/*');
|
||||||
} else {
|
} else {
|
||||||
$future = $repository->getRemoteCommandFuture(
|
$future = $repository->getRemoteCommandFuture(
|
||||||
'fetch --all --prune');
|
'fetch --all --prune');
|
||||||
|
|
|
@ -25,29 +25,31 @@ final class PhabricatorRepositoryRefEngine
|
||||||
switch ($vcs) {
|
switch ($vcs) {
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_SVN:
|
||||||
// No meaningful refs of any type in Subversion.
|
// No meaningful refs of any type in Subversion.
|
||||||
$branches = array();
|
$maps = array();
|
||||||
$bookmarks = array();
|
|
||||||
$tags = array();
|
|
||||||
break;
|
break;
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_MERCURIAL:
|
||||||
$branches = $this->loadMercurialBranchPositions($repository);
|
$branches = $this->loadMercurialBranchPositions($repository);
|
||||||
$bookmarks = $this->loadMercurialBookmarkPositions($repository);
|
$bookmarks = $this->loadMercurialBookmarkPositions($repository);
|
||||||
$tags = array();
|
$maps = array(
|
||||||
|
PhabricatorRepositoryRefCursor::TYPE_BRANCH => $branches,
|
||||||
|
PhabricatorRepositoryRefCursor::TYPE_BOOKMARK => $bookmarks,
|
||||||
|
);
|
||||||
|
|
||||||
$branches_may_close = true;
|
$branches_may_close = true;
|
||||||
break;
|
break;
|
||||||
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
case PhabricatorRepositoryType::REPOSITORY_TYPE_GIT:
|
||||||
$branches = $this->loadGitBranchPositions($repository);
|
$maps = $this->loadGitRefPositions($repository);
|
||||||
$bookmarks = array();
|
|
||||||
$tags = $this->loadGitTagPositions($repository);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new Exception(pht('Unknown VCS "%s"!', $vcs));
|
throw new Exception(pht('Unknown VCS "%s"!', $vcs));
|
||||||
}
|
}
|
||||||
|
|
||||||
$maps = array(
|
// Fill in any missing types with empty lists.
|
||||||
PhabricatorRepositoryRefCursor::TYPE_BRANCH => $branches,
|
$maps = $maps + array(
|
||||||
PhabricatorRepositoryRefCursor::TYPE_TAG => $tags,
|
PhabricatorRepositoryRefCursor::TYPE_BRANCH => array(),
|
||||||
PhabricatorRepositoryRefCursor::TYPE_BOOKMARK => $bookmarks,
|
PhabricatorRepositoryRefCursor::TYPE_TAG => array(),
|
||||||
|
PhabricatorRepositoryRefCursor::TYPE_BOOKMARK => array(),
|
||||||
|
PhabricatorRepositoryRefCursor::TYPE_REF => array(),
|
||||||
);
|
);
|
||||||
|
|
||||||
$all_cursors = id(new PhabricatorRepositoryRefCursorQuery())
|
$all_cursors = id(new PhabricatorRepositoryRefCursorQuery())
|
||||||
|
@ -83,6 +85,13 @@ final class PhabricatorRepositoryRefEngine
|
||||||
$ref->save();
|
$ref->save();
|
||||||
}
|
}
|
||||||
foreach ($this->deadRefs as $ref) {
|
foreach ($this->deadRefs as $ref) {
|
||||||
|
// Shove this ref into the old refs table so the discovery engine
|
||||||
|
// can check if any commits have been rendered unreachable.
|
||||||
|
id(new PhabricatorRepositoryOldRef())
|
||||||
|
->setRepositoryPHID($repository->getPHID())
|
||||||
|
->setCommitIdentifier($ref->getCommitIdentifier())
|
||||||
|
->save();
|
||||||
|
|
||||||
$ref->delete();
|
$ref->delete();
|
||||||
}
|
}
|
||||||
$repository->saveTransaction();
|
$repository->saveTransaction();
|
||||||
|
@ -91,6 +100,7 @@ final class PhabricatorRepositoryRefEngine
|
||||||
$this->deadRefs = array();
|
$this->deadRefs = array();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$branches = $maps[PhabricatorRepositoryRefCursor::TYPE_BRANCH];
|
||||||
if ($branches && $branches_may_close) {
|
if ($branches && $branches_may_close) {
|
||||||
$this->updateBranchStates($repository, $branches);
|
$this->updateBranchStates($repository, $branches);
|
||||||
}
|
}
|
||||||
|
@ -449,28 +459,12 @@ final class PhabricatorRepositoryRefEngine
|
||||||
/**
|
/**
|
||||||
* @task git
|
* @task git
|
||||||
*/
|
*/
|
||||||
private function loadGitBranchPositions(PhabricatorRepository $repository) {
|
private function loadGitRefPositions(PhabricatorRepository $repository) {
|
||||||
return id(new DiffusionLowLevelGitRefQuery())
|
$refs = id(new DiffusionLowLevelGitRefQuery())
|
||||||
->setRepository($repository)
|
->setRepository($repository)
|
||||||
->withRefTypes(
|
|
||||||
array(
|
|
||||||
PhabricatorRepositoryRefCursor::TYPE_BRANCH,
|
|
||||||
))
|
|
||||||
->execute();
|
->execute();
|
||||||
}
|
|
||||||
|
|
||||||
|
return mgroup($refs, 'getRefType');
|
||||||
/**
|
|
||||||
* @task git
|
|
||||||
*/
|
|
||||||
private function loadGitTagPositions(PhabricatorRepository $repository) {
|
|
||||||
return id(new DiffusionLowLevelGitRefQuery())
|
|
||||||
->setRepository($repository)
|
|
||||||
->withRefTypes(
|
|
||||||
array(
|
|
||||||
PhabricatorRepositoryRefCursor::TYPE_TAG,
|
|
||||||
))
|
|
||||||
->execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,103 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
final class PhabricatorRepositoryManagementMarkReachableWorkflow
|
||||||
|
extends PhabricatorRepositoryManagementWorkflow {
|
||||||
|
|
||||||
|
private $untouchedCount = 0;
|
||||||
|
|
||||||
|
protected function didConstruct() {
|
||||||
|
$this
|
||||||
|
->setName('mark-reachable')
|
||||||
|
->setExamples('**mark-reachable** [__options__] __repository__ ...')
|
||||||
|
->setSynopsis(
|
||||||
|
pht(
|
||||||
|
'Rebuild "unreachable" flags for commits in __repository__.'))
|
||||||
|
->setArguments(
|
||||||
|
array(
|
||||||
|
array(
|
||||||
|
'name' => 'repos',
|
||||||
|
'wildcard' => true,
|
||||||
|
),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function execute(PhutilArgumentParser $args) {
|
||||||
|
$repos = $this->loadRepositories($args, 'repos');
|
||||||
|
if (!$repos) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Specify one or more repositories to correct reachability status '.
|
||||||
|
'for.'));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach ($repos as $repo) {
|
||||||
|
$this->markReachable($repo);
|
||||||
|
}
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Examined %s commits already in the correct state.',
|
||||||
|
new PhutilNumber($this->untouchedCount)));
|
||||||
|
|
||||||
|
echo tsprintf(
|
||||||
|
"%s\n",
|
||||||
|
pht('Done.'));
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function markReachable(PhabricatorRepository $repository) {
|
||||||
|
if (!$repository->isGit()) {
|
||||||
|
throw new PhutilArgumentUsageException(
|
||||||
|
pht(
|
||||||
|
'Only Git repositories are supported, this repository ("%s") is '.
|
||||||
|
'not a Git repository.',
|
||||||
|
$repository->getDisplayName()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$viewer = $this->getViewer();
|
||||||
|
|
||||||
|
$commits = id(new DiffusionCommitQuery())
|
||||||
|
->setViewer($viewer)
|
||||||
|
->withRepository($repository)
|
||||||
|
->execute();
|
||||||
|
|
||||||
|
$flag = PhabricatorRepositoryCommit::IMPORTED_UNREACHABLE;
|
||||||
|
|
||||||
|
$graph = new PhabricatorGitGraphStream($repository);
|
||||||
|
foreach ($commits as $commit) {
|
||||||
|
$identifier = $commit->getCommitIdentifier();
|
||||||
|
|
||||||
|
try {
|
||||||
|
$graph->getCommitDate($identifier);
|
||||||
|
$unreachable = false;
|
||||||
|
} catch (Exception $ex) {
|
||||||
|
$unreachable = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// The commit has proper reachability, so do nothing.
|
||||||
|
if ($commit->isUnreachable() === $unreachable) {
|
||||||
|
$this->untouchedCount++;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($unreachable) {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s: %s\n",
|
||||||
|
$commit->getMonogram(),
|
||||||
|
pht('Marking commit unreachable.'));
|
||||||
|
|
||||||
|
$commit->writeImportStatusFlag($flag);
|
||||||
|
} else {
|
||||||
|
echo tsprintf(
|
||||||
|
"%s: %s\n",
|
||||||
|
$commit->getMonogram(),
|
||||||
|
pht('Marking commit reachable.'));
|
||||||
|
|
||||||
|
$commit->clearImportStatusFlag($flag);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -46,6 +46,10 @@ final class PhabricatorRepositoryCommitPHIDType extends PhabricatorPHIDType {
|
||||||
$handle->setFullName($full_name);
|
$handle->setFullName($full_name);
|
||||||
$handle->setURI($commit->getURI());
|
$handle->setURI($commit->getURI());
|
||||||
$handle->setTimestamp($commit->getEpoch());
|
$handle->setTimestamp($commit->getEpoch());
|
||||||
|
|
||||||
|
if ($commit->isUnreachable()) {
|
||||||
|
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -910,6 +910,21 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function shouldTrackRef(DiffusionRepositoryRef $ref) {
|
||||||
|
// At least for now, don't track the staging area tags.
|
||||||
|
if ($ref->isTag()) {
|
||||||
|
if (preg_match('(^phabricator/)', $ref->getShortName())) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!$ref->isBranch()) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->shouldTrackBranch($ref->getShortName());
|
||||||
|
}
|
||||||
|
|
||||||
public function shouldTrackBranch($branch) {
|
public function shouldTrackBranch($branch) {
|
||||||
return $this->isBranchInFilter($branch, 'branch-filter');
|
return $this->isBranchInFilter($branch, 'branch-filter');
|
||||||
}
|
}
|
||||||
|
@ -1020,6 +1035,14 @@ final class PhabricatorRepository extends PhabricatorRepositoryDAO
|
||||||
/* -( Autoclose )---------------------------------------------------------- */
|
/* -( Autoclose )---------------------------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function shouldAutocloseRef(DiffusionRepositoryRef $ref) {
|
||||||
|
if (!$ref->isBranch()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $this->shouldAutocloseBranch($ref->getShortName());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Determine if autoclose is active for a branch.
|
* Determine if autoclose is active for a branch.
|
||||||
*
|
*
|
||||||
|
|
|
@ -32,6 +32,7 @@ final class PhabricatorRepositoryCommit
|
||||||
const IMPORTED_ALL = 15;
|
const IMPORTED_ALL = 15;
|
||||||
|
|
||||||
const IMPORTED_CLOSEABLE = 1024;
|
const IMPORTED_CLOSEABLE = 1024;
|
||||||
|
const IMPORTED_UNREACHABLE = 2048;
|
||||||
|
|
||||||
private $commitData = self::ATTACHABLE;
|
private $commitData = self::ATTACHABLE;
|
||||||
private $audits = self::ATTACHABLE;
|
private $audits = self::ATTACHABLE;
|
||||||
|
@ -58,14 +59,43 @@ final class PhabricatorRepositoryCommit
|
||||||
return $this->isPartiallyImported(self::IMPORTED_ALL);
|
return $this->isPartiallyImported(self::IMPORTED_ALL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function isUnreachable() {
|
||||||
|
return $this->isPartiallyImported(self::IMPORTED_UNREACHABLE);
|
||||||
|
}
|
||||||
|
|
||||||
public function writeImportStatusFlag($flag) {
|
public function writeImportStatusFlag($flag) {
|
||||||
queryfx(
|
return $this->adjustImportStatusFlag($flag, true);
|
||||||
$this->establishConnection('w'),
|
}
|
||||||
'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d',
|
|
||||||
$this->getTableName(),
|
public function clearImportStatusFlag($flag) {
|
||||||
$flag,
|
return $this->adjustImportStatusFlag($flag, false);
|
||||||
$this->getID());
|
}
|
||||||
$this->setImportStatus($this->getImportStatus() | $flag);
|
|
||||||
|
private function adjustImportStatusFlag($flag, $set) {
|
||||||
|
$conn_w = $this->establishConnection('w');
|
||||||
|
$table_name = $this->getTableName();
|
||||||
|
$id = $this->getID();
|
||||||
|
|
||||||
|
if ($set) {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET importStatus = (importStatus | %d) WHERE id = %d',
|
||||||
|
$table_name,
|
||||||
|
$flag,
|
||||||
|
$id);
|
||||||
|
|
||||||
|
$this->setImportStatus($this->getImportStatus() | $flag);
|
||||||
|
} else {
|
||||||
|
queryfx(
|
||||||
|
$conn_w,
|
||||||
|
'UPDATE %T SET importStatus = (importStatus & ~%d) WHERE id = %d',
|
||||||
|
$table_name,
|
||||||
|
$flag,
|
||||||
|
$id);
|
||||||
|
|
||||||
|
$this->setImportStatus($this->getImportStatus() & ~$flag);
|
||||||
|
}
|
||||||
|
|
||||||
return $this;
|
return $this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores outdated refs which need to be checked for reachability.
|
||||||
|
*
|
||||||
|
* When a branch is deleted, the old HEAD ends up here and the discovery
|
||||||
|
* engine marks all the commits that previously appeared on it as unreachable.
|
||||||
|
*/
|
||||||
|
final class PhabricatorRepositoryOldRef
|
||||||
|
extends PhabricatorRepositoryDAO
|
||||||
|
implements PhabricatorPolicyInterface {
|
||||||
|
|
||||||
|
protected $repositoryPHID;
|
||||||
|
protected $commitIdentifier;
|
||||||
|
|
||||||
|
protected function getConfiguration() {
|
||||||
|
return array(
|
||||||
|
self::CONFIG_TIMESTAMPS => false,
|
||||||
|
self::CONFIG_COLUMN_SCHEMA => array(
|
||||||
|
'commitIdentifier' => 'text40',
|
||||||
|
),
|
||||||
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
|
'key_repository' => array(
|
||||||
|
'columns' => array('repositoryPHID'),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
) + parent::getConfiguration();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* -( PhabricatorPolicyInterface )----------------------------------------- */
|
||||||
|
|
||||||
|
|
||||||
|
public function getCapabilities() {
|
||||||
|
return array(
|
||||||
|
PhabricatorPolicyCapability::CAN_VIEW,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getPolicy($capability) {
|
||||||
|
return PhabricatorPolicies::getMostOpenPolicy();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function describeAutomaticCapability($capability) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -12,6 +12,7 @@ final class PhabricatorRepositoryRefCursor
|
||||||
const TYPE_BRANCH = 'branch';
|
const TYPE_BRANCH = 'branch';
|
||||||
const TYPE_TAG = 'tag';
|
const TYPE_TAG = 'tag';
|
||||||
const TYPE_BOOKMARK = 'bookmark';
|
const TYPE_BOOKMARK = 'bookmark';
|
||||||
|
const TYPE_REF = 'ref';
|
||||||
|
|
||||||
protected $repositoryPHID;
|
protected $repositoryPHID;
|
||||||
protected $refType;
|
protected $refType;
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
final class PhabricatorRepositoryCommitHeraldWorker
|
final class PhabricatorRepositoryCommitHeraldWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return PhabricatorRepositoryCommit::IMPORTED_HERALD;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequiredLeaseTime() {
|
public function getRequiredLeaseTime() {
|
||||||
// Herald rules may take a long time to process.
|
// Herald rules may take a long time to process.
|
||||||
return phutil_units('4 hours in seconds');
|
return phutil_units('4 hours in seconds');
|
||||||
|
@ -12,6 +16,12 @@ final class PhabricatorRepositoryCommitHeraldWorker
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
|
|
||||||
|
if ($this->shouldSkipImportStep()) {
|
||||||
|
// This worker has no followup tasks, so we can just bail out
|
||||||
|
// right away without queueing anything.
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Reload the commit to pull commit data and audit requests.
|
// Reload the commit to pull commit data and audit requests.
|
||||||
$commit = id(new DiffusionCommitQuery())
|
$commit = id(new DiffusionCommitQuery())
|
||||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||||
|
|
|
@ -3,14 +3,18 @@
|
||||||
final class PhabricatorRepositoryCommitOwnersWorker
|
final class PhabricatorRepositoryCommitOwnersWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return PhabricatorRepositoryCommit::IMPORTED_OWNERS;
|
||||||
|
}
|
||||||
|
|
||||||
protected function parseCommit(
|
protected function parseCommit(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
|
|
||||||
$this->triggerOwnerAudits($repository, $commit);
|
if (!$this->shouldSkipImportStep()) {
|
||||||
|
$this->triggerOwnerAudits($repository, $commit);
|
||||||
$commit->writeImportStatusFlag(
|
$commit->writeImportStatusFlag($this->getImportStepFlag());
|
||||||
PhabricatorRepositoryCommit::IMPORTED_OWNERS);
|
}
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
if ($this->shouldQueueFollowupTasks()) {
|
||||||
$this->queueTask(
|
$this->queueTask(
|
||||||
|
|
|
@ -26,6 +26,14 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
pht('Commit "%s" does not exist.', $commit_id));
|
pht('Commit "%s" does not exist.', $commit_id));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($commit->isUnreachable()) {
|
||||||
|
throw new PhabricatorWorkerPermanentFailureException(
|
||||||
|
pht(
|
||||||
|
'Commit "%s" has been deleted: it is no longer reachable from '.
|
||||||
|
'any ref.',
|
||||||
|
$commit_id));
|
||||||
|
}
|
||||||
|
|
||||||
$this->commit = $commit;
|
$this->commit = $commit;
|
||||||
|
|
||||||
return $commit;
|
return $commit;
|
||||||
|
@ -44,6 +52,42 @@ abstract class PhabricatorRepositoryCommitParserWorker
|
||||||
return !idx($this->getTaskData(), 'only');
|
return !idx($this->getTaskData(), 'only');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
final protected function shouldSkipImportStep() {
|
||||||
|
// If this step has already been performed and this is a "natural" task
|
||||||
|
// which was queued by the normal daemons, decline to do the work again.
|
||||||
|
// This mitigates races if commits are rapidly deleted and revived.
|
||||||
|
$flag = $this->getImportStepFlag();
|
||||||
|
if (!$flag) {
|
||||||
|
// This step doesn't have an associated flag.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit = $this->commit;
|
||||||
|
if (!$commit->isPartiallyImported($flag)) {
|
||||||
|
// This commit doesn't have the flag set yet.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (!$this->shouldQueueFollowupTasks()) {
|
||||||
|
// This task was queued by administrative tools, so do the work even
|
||||||
|
// if it duplicates existing work.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->log(
|
||||||
|
"%s\n",
|
||||||
|
pht(
|
||||||
|
'Skipping import step; this step was previously completed for '.
|
||||||
|
'this commit.'));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
abstract protected function parseCommit(
|
abstract protected function parseCommit(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit);
|
PhabricatorRepositoryCommit $commit);
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
abstract class PhabricatorRepositoryCommitChangeParserWorker
|
abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
|
protected function getImportStepFlag() {
|
||||||
|
return PhabricatorRepositoryCommit::IMPORTED_CHANGE;
|
||||||
|
}
|
||||||
|
|
||||||
public function getRequiredLeaseTime() {
|
public function getRequiredLeaseTime() {
|
||||||
// It can take a very long time to parse commits; some commits in the
|
// It can take a very long time to parse commits; some commits in the
|
||||||
// Facebook repository affect many millions of paths. Acquire 24h leases.
|
// Facebook repository affect many millions of paths. Acquire 24h leases.
|
||||||
|
@ -23,9 +27,15 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
$results = $this->parseCommitChanges($repository, $commit);
|
if (!$this->shouldSkipImportStep()) {
|
||||||
if ($results) {
|
$results = $this->parseCommitChanges($repository, $commit);
|
||||||
$this->writeCommitChanges($repository, $commit, $results);
|
if ($results) {
|
||||||
|
$this->writeCommitChanges($repository, $commit, $results);
|
||||||
|
}
|
||||||
|
|
||||||
|
$commit->writeImportStatusFlag($this->getImportStepFlag());
|
||||||
|
|
||||||
|
PhabricatorSearchWorker::queueDocumentForIndexing($commit->getPHID());
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->finishParse();
|
$this->finishParse();
|
||||||
|
@ -85,12 +95,6 @@ abstract class PhabricatorRepositoryCommitChangeParserWorker
|
||||||
|
|
||||||
protected function finishParse() {
|
protected function finishParse() {
|
||||||
$commit = $this->commit;
|
$commit = $this->commit;
|
||||||
|
|
||||||
$commit->writeImportStatusFlag(
|
|
||||||
PhabricatorRepositoryCommit::IMPORTED_CHANGE);
|
|
||||||
|
|
||||||
PhabricatorSearchWorker::queueDocumentForIndexing($commit->getPHID());
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
if ($this->shouldQueueFollowupTasks()) {
|
||||||
$this->queueTask(
|
$this->queueTask(
|
||||||
'PhabricatorRepositoryCommitOwnersWorker',
|
'PhabricatorRepositoryCommitOwnersWorker',
|
||||||
|
|
|
@ -3,42 +3,52 @@
|
||||||
abstract class PhabricatorRepositoryCommitMessageParserWorker
|
abstract class PhabricatorRepositoryCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitParserWorker {
|
extends PhabricatorRepositoryCommitParserWorker {
|
||||||
|
|
||||||
abstract protected function parseCommitWithRef(
|
protected function getImportStepFlag() {
|
||||||
PhabricatorRepository $repository,
|
return PhabricatorRepositoryCommit::IMPORTED_MESSAGE;
|
||||||
PhabricatorRepositoryCommit $commit,
|
}
|
||||||
DiffusionCommitRef $ref);
|
|
||||||
|
abstract protected function getFollowupTaskClass();
|
||||||
|
|
||||||
final protected function parseCommit(
|
final protected function parseCommit(
|
||||||
PhabricatorRepository $repository,
|
PhabricatorRepository $repository,
|
||||||
PhabricatorRepositoryCommit $commit) {
|
PhabricatorRepositoryCommit $commit) {
|
||||||
|
|
||||||
$viewer = PhabricatorUser::getOmnipotentUser();
|
if (!$this->shouldSkipImportStep()) {
|
||||||
|
$viewer = PhabricatorUser::getOmnipotentUser();
|
||||||
|
|
||||||
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
|
$refs_raw = DiffusionQuery::callConduitWithDiffusionRequest(
|
||||||
$viewer,
|
$viewer,
|
||||||
DiffusionRequest::newFromDictionary(
|
DiffusionRequest::newFromDictionary(
|
||||||
|
array(
|
||||||
|
'repository' => $repository,
|
||||||
|
'user' => $viewer,
|
||||||
|
)),
|
||||||
|
'diffusion.querycommits',
|
||||||
array(
|
array(
|
||||||
'repository' => $repository,
|
'repositoryPHID' => $repository->getPHID(),
|
||||||
'user' => $viewer,
|
'phids' => array($commit->getPHID()),
|
||||||
)),
|
'bypassCache' => true,
|
||||||
'diffusion.querycommits',
|
'needMessages' => true,
|
||||||
array(
|
));
|
||||||
'repositoryPHID' => $repository->getPHID(),
|
|
||||||
'phids' => array($commit->getPHID()),
|
|
||||||
'bypassCache' => true,
|
|
||||||
'needMessages' => true,
|
|
||||||
));
|
|
||||||
|
|
||||||
if (empty($refs_raw['data'])) {
|
if (empty($refs_raw['data'])) {
|
||||||
throw new Exception(
|
throw new Exception(
|
||||||
pht(
|
pht(
|
||||||
'Unable to retrieve details for commit "%s"!',
|
'Unable to retrieve details for commit "%s"!',
|
||||||
$commit->getPHID()));
|
$commit->getPHID()));
|
||||||
|
}
|
||||||
|
|
||||||
|
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
|
||||||
|
$this->updateCommitData($ref);
|
||||||
}
|
}
|
||||||
|
|
||||||
$ref = DiffusionCommitRef::newFromConduitResult(head($refs_raw['data']));
|
if ($this->shouldQueueFollowupTasks()) {
|
||||||
|
$this->queueTask(
|
||||||
$this->parseCommitWithRef($repository, $commit, $ref);
|
$this->getFollowupTaskClass(),
|
||||||
|
array(
|
||||||
|
'commitID' => $commit->getID(),
|
||||||
|
));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
final protected function updateCommitData(DiffusionCommitRef $ref) {
|
final protected function updateCommitData(DiffusionCommitRef $ref) {
|
||||||
|
|
|
@ -3,20 +3,8 @@
|
||||||
final class PhabricatorRepositoryGitCommitMessageParserWorker
|
final class PhabricatorRepositoryGitCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitMessageParserWorker {
|
extends PhabricatorRepositoryCommitMessageParserWorker {
|
||||||
|
|
||||||
protected function parseCommitWithRef(
|
protected function getFollowupTaskClass() {
|
||||||
PhabricatorRepository $repository,
|
return 'PhabricatorRepositoryGitCommitChangeParserWorker';
|
||||||
PhabricatorRepositoryCommit $commit,
|
|
||||||
DiffusionCommitRef $ref) {
|
|
||||||
|
|
||||||
$this->updateCommitData($ref);
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositoryGitCommitChangeParserWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,8 @@
|
||||||
final class PhabricatorRepositoryMercurialCommitMessageParserWorker
|
final class PhabricatorRepositoryMercurialCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitMessageParserWorker {
|
extends PhabricatorRepositoryCommitMessageParserWorker {
|
||||||
|
|
||||||
protected function parseCommitWithRef(
|
protected function getFollowupTaskClass() {
|
||||||
PhabricatorRepository $repository,
|
return 'PhabricatorRepositoryMercurialCommitChangeParserWorker';
|
||||||
PhabricatorRepositoryCommit $commit,
|
|
||||||
DiffusionCommitRef $ref) {
|
|
||||||
|
|
||||||
$this->updateCommitData($ref);
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositoryMercurialCommitChangeParserWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,20 +3,8 @@
|
||||||
final class PhabricatorRepositorySvnCommitMessageParserWorker
|
final class PhabricatorRepositorySvnCommitMessageParserWorker
|
||||||
extends PhabricatorRepositoryCommitMessageParserWorker {
|
extends PhabricatorRepositoryCommitMessageParserWorker {
|
||||||
|
|
||||||
protected function parseCommitWithRef(
|
protected function getFollowupTaskClass() {
|
||||||
PhabricatorRepository $repository,
|
return 'PhabricatorRepositorySvnCommitChangeParserWorker';
|
||||||
PhabricatorRepositoryCommit $commit,
|
|
||||||
DiffusionCommitRef $ref) {
|
|
||||||
|
|
||||||
$this->updateCommitData($ref);
|
|
||||||
|
|
||||||
if ($this->shouldQueueFollowupTasks()) {
|
|
||||||
$this->queueTask(
|
|
||||||
'PhabricatorRepositorySvnCommitChangeParserWorker',
|
|
||||||
array(
|
|
||||||
'commitID' => $commit->getID(),
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue