1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-10 06:41:04 +01:00

(stable) Promote 2018 Week 35

This commit is contained in:
epriestley 2018-09-04 10:15:49 -07:00
commit b0130f6d4e
93 changed files with 1544 additions and 681 deletions

View file

@ -9,8 +9,8 @@ return array(
'names' => array(
'conpherence.pkg.css' => 'e68cf1fa',
'conpherence.pkg.js' => '15191c65',
'core.pkg.css' => 'fc4839c8',
'core.pkg.js' => '2058ec09',
'core.pkg.css' => 'badf3f16',
'core.pkg.js' => 'b5a949ca',
'differential.pkg.css' => '06dc617c',
'differential.pkg.js' => 'c1cfa143',
'diffusion.pkg.css' => 'a2d17c7d',
@ -75,7 +75,7 @@ return array(
'rsrc/css/application/feed/feed.css' => 'ecd4ec57',
'rsrc/css/application/files/global-drag-and-drop.css' => 'b556a948',
'rsrc/css/application/flag/flag.css' => 'bba8f811',
'rsrc/css/application/harbormaster/harbormaster.css' => '730a4a3c',
'rsrc/css/application/harbormaster/harbormaster.css' => '7446ce72',
'rsrc/css/application/herald/herald-test.css' => 'a52e323e',
'rsrc/css/application/herald/herald.css' => 'cd8d0134',
'rsrc/css/application/maniphest/report.css' => '9b9580b7',
@ -146,15 +146,15 @@ return array(
'rsrc/css/phui/phui-comment-panel.css' => 'f50152ad',
'rsrc/css/phui/phui-crumbs-view.css' => '10728aaa',
'rsrc/css/phui/phui-curtain-view.css' => '2bdaf026',
'rsrc/css/phui/phui-document-pro.css' => '8af7ea27',
'rsrc/css/phui/phui-document-pro.css' => '0e41dd91',
'rsrc/css/phui/phui-document-summary.css' => '9ca48bdf',
'rsrc/css/phui/phui-document.css' => '878c2f52',
'rsrc/css/phui/phui-document.css' => 'c4ac41f9',
'rsrc/css/phui/phui-feed-story.css' => '44a9c8e9',
'rsrc/css/phui/phui-fontkit.css' => '1320ed01',
'rsrc/css/phui/phui-form-view.css' => 'f808e5be',
'rsrc/css/phui/phui-form.css' => '7aaa04e3',
'rsrc/css/phui/phui-head-thing.css' => 'fd311e5f',
'rsrc/css/phui/phui-header-view.css' => 'edeb9252',
'rsrc/css/phui/phui-header-view.css' => '1ba8b707',
'rsrc/css/phui/phui-hovercard.css' => 'f0592bcf',
'rsrc/css/phui/phui-icon-set-selector.css' => '87db8fee',
'rsrc/css/phui/phui-icon.css' => 'cf24ceec',
@ -473,7 +473,7 @@ return array(
'rsrc/js/core/behavior-more.js' => 'a80d0378',
'rsrc/js/core/behavior-object-selector.js' => '77c1f0b0',
'rsrc/js/core/behavior-oncopy.js' => '2926fff2',
'rsrc/js/core/behavior-phabricator-nav.js' => '94b7c320',
'rsrc/js/core/behavior-phabricator-nav.js' => '9d32bc88',
'rsrc/js/core/behavior-phabricator-remarkup-assist.js' => 'acd29eee',
'rsrc/js/core/behavior-read-only-warning.js' => 'ba158207',
'rsrc/js/core/behavior-redirect.js' => '0213259f',
@ -554,7 +554,7 @@ return array(
'font-fontawesome' => 'e838e088',
'font-lato' => 'c7ccd872',
'global-drag-and-drop-css' => 'b556a948',
'harbormaster-css' => '730a4a3c',
'harbormaster-css' => '7446ce72',
'herald-css' => 'cd8d0134',
'herald-rule-editor' => 'dca75c0e',
'herald-test-css' => 'a52e323e',
@ -631,7 +631,7 @@ return array(
'javelin-behavior-phabricator-keyboard-pager' => 'a8da01f0',
'javelin-behavior-phabricator-keyboard-shortcuts' => '01fca1f0',
'javelin-behavior-phabricator-line-linker' => '66a62306',
'javelin-behavior-phabricator-nav' => '94b7c320',
'javelin-behavior-phabricator-nav' => '9d32bc88',
'javelin-behavior-phabricator-notification-example' => '8ce821c5',
'javelin-behavior-phabricator-object-selector' => '77c1f0b0',
'javelin-behavior-phabricator-oncopy' => '2926fff2',
@ -813,15 +813,15 @@ return array(
'phui-crumbs-view-css' => '10728aaa',
'phui-curtain-view-css' => '2bdaf026',
'phui-document-summary-view-css' => '9ca48bdf',
'phui-document-view-css' => '878c2f52',
'phui-document-view-pro-css' => '8af7ea27',
'phui-document-view-css' => 'c4ac41f9',
'phui-document-view-pro-css' => '0e41dd91',
'phui-feed-story-css' => '44a9c8e9',
'phui-font-icon-base-css' => '870a7360',
'phui-fontkit-css' => '1320ed01',
'phui-form-css' => '7aaa04e3',
'phui-form-view-css' => 'f808e5be',
'phui-head-thing-view-css' => 'fd311e5f',
'phui-header-view-css' => 'edeb9252',
'phui-header-view-css' => '1ba8b707',
'phui-hovercard' => '1bd28176',
'phui-hovercard-view-css' => 'f0592bcf',
'phui-icon-set-selector-css' => '87db8fee',
@ -1628,16 +1628,6 @@ return array(
'javelin-resource',
'javelin-routable',
),
'94b7c320' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-dom',
'javelin-magical-init',
'javelin-vector',
'javelin-request',
'javelin-util',
),
'960f6a39' => array(
'javelin-behavior',
'javelin-dom',
@ -1658,6 +1648,16 @@ return array(
'javelin-workflow',
'javelin-stratcom',
),
'9d32bc88' => array(
'javelin-behavior',
'javelin-behavior-device',
'javelin-stratcom',
'javelin-dom',
'javelin-magical-init',
'javelin-vector',
'javelin-request',
'javelin-util',
),
'9d9685d6' => array(
'phui-oi-list-view-css',
),

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_drydock.drydock_lease
ADD acquiredEpoch INT UNSIGNED;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_drydock.drydock_lease
ADD activatedEpoch INT UNSIGNED;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
ADD contentPHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_content
ADD documentPHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
ADD editedEpoch INT UNSIGNED NOT NULL;

View file

@ -0,0 +1,57 @@
<?php
// Update the PhrictionDocument and PhrictionContent tables to refer to one
// another by PHID instead of by ID.
$document_table = new PhrictionDocument();
$content_table = new PhrictionContent();
$conn = $document_table->establishConnection('w');
$document_iterator = new LiskRawMigrationIterator(
$conn,
$document_table->getTableName());
foreach ($document_iterator as $row) {
$content_id = $row['contentID'];
$content_row = queryfx_one(
$conn,
'SELECT phid, dateCreated FROM %T WHERE id = %d',
$content_table->getTableName(),
$content_id);
if (!$content_row) {
continue;
}
queryfx(
$conn,
'UPDATE %T SET contentPHID = %s, editedEpoch = %d WHERE id = %d',
$document_table->getTableName(),
$content_row['phid'],
$content_row['dateCreated'],
$row['id']);
}
$content_iterator = new LiskRawMigrationIterator(
$conn,
$content_table->getTableName());
foreach ($content_iterator as $row) {
$document_id = $row['documentID'];
$document_row = queryfx_one(
$conn,
'SELECT phid FROM %T WHERE id = %d',
$document_table->getTableName(),
$document_id);
if (!$document_row) {
continue;
}
queryfx(
$conn,
'UPDATE %T SET documentPHID = %s WHERE id = %d',
$content_table->getTableName(),
$document_row['phid'],
$row['id']);
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
DROP contentID;

View file

@ -0,0 +1,20 @@
<?php
// See T13193. We're about to drop the "documentID" column, which is part of
// a UNIQUE KEY. In MariaDB, we must first drop the "documentID" key or we get
// into deep trouble.
// There's no "IF EXISTS" modifier for "ALTER TABLE" so run this as a PHP patch
// instead of an SQL patch.
$table = new PhrictionContent();
$conn = $table->establishConnection('w');
try {
queryfx(
$conn,
'ALTER TABLE %T DROP KEY documentID',
$table->getTableName());
} catch (AphrontQueryException $ex) {
// Ignore.
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_content
DROP documentID;

View file

@ -0,0 +1 @@
DELETE FROM {$NAMESPACE}_phriction.phriction_content WHERE documentPHID = '';

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_content
ADD UNIQUE KEY `key_version` (documentPHID, version);

View file

@ -0,0 +1,26 @@
<?php
$document_table = new PhrictionDocument();
$document_conn = $document_table->establishConnection('w');
$document_name = $document_table->getTableName();
$properties_table = new PhabricatorMetaMTAMailProperties();
$conn = $properties_table->establishConnection('w');
$iterator = new LiskRawMigrationIterator($document_conn, $document_name);
foreach ($iterator as $row) {
queryfx(
$conn,
'INSERT IGNORE INTO %T
(objectPHID, mailProperties, dateCreated, dateModified)
VALUES
(%s, %s, %d, %d)',
$properties_table->getTableName(),
$row['phid'],
phutil_json_encode(
array(
'mailKey' => $row['mailKey'],
)),
PhabricatorTime::getNow(),
PhabricatorTime::getNow());
}

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
DROP mailKey;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
ADD maxVersion INT UNSIGNED NOT NULL;

View file

@ -0,0 +1,30 @@
<?php
// Populate the "maxVersion" column by copying the maximum "version" from the
// content table.
$document_table = new PhrictionDocument();
$content_table = new PhrictionContent();
$conn = $document_table->establishConnection('w');
$iterator = new LiskRawMigrationIterator(
$conn,
$document_table->getTableName());
foreach ($iterator as $row) {
$content = queryfx_one(
$conn,
'SELECT MAX(version) max FROM %T WHERE documentPHID = %s',
$content_table->getTableName(),
$row['phid']);
if (!$content) {
continue;
}
queryfx(
$conn,
'UPDATE %T SET maxVersion = %d WHERE id = %d',
$document_table->getTableName(),
$content['max'],
$row['id']);
}

View file

@ -644,6 +644,7 @@ phutil_register_library_map(array(
'DifferentialRevisionStatusTransaction' => 'applications/differential/xaction/DifferentialRevisionStatusTransaction.php',
'DifferentialRevisionSummaryHeraldField' => 'applications/differential/herald/DifferentialRevisionSummaryHeraldField.php',
'DifferentialRevisionSummaryTransaction' => 'applications/differential/xaction/DifferentialRevisionSummaryTransaction.php',
'DifferentialRevisionTestPlanHeraldField' => 'applications/differential/herald/DifferentialRevisionTestPlanHeraldField.php',
'DifferentialRevisionTestPlanTransaction' => 'applications/differential/xaction/DifferentialRevisionTestPlanTransaction.php',
'DifferentialRevisionTitleHeraldField' => 'applications/differential/herald/DifferentialRevisionTitleHeraldField.php',
'DifferentialRevisionTitleTransaction' => 'applications/differential/xaction/DifferentialRevisionTitleTransaction.php',
@ -1912,7 +1913,7 @@ phutil_register_library_map(array(
'PHUIDiffTableOfContentsListView' => 'infrastructure/diff/view/PHUIDiffTableOfContentsListView.php',
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'infrastructure/diff/view/PHUIDiffTwoUpInlineCommentRowScaffold.php',
'PHUIDocumentSummaryView' => 'view/phui/PHUIDocumentSummaryView.php',
'PHUIDocumentViewPro' => 'view/phui/PHUIDocumentViewPro.php',
'PHUIDocumentView' => 'view/phui/PHUIDocumentView.php',
'PHUIFeedStoryExample' => 'applications/uiexample/examples/PHUIFeedStoryExample.php',
'PHUIFeedStoryView' => 'view/phui/PHUIFeedStoryView.php',
'PHUIFormDividerControl' => 'view/form/control/PHUIFormDividerControl.php',
@ -2042,6 +2043,7 @@ phutil_register_library_map(array(
'PasteSearchConduitAPIMethod' => 'applications/paste/conduit/PasteSearchConduitAPIMethod.php',
'PeopleBrowseUserDirectoryCapability' => 'applications/people/capability/PeopleBrowseUserDirectoryCapability.php',
'PeopleCreateUsersCapability' => 'applications/people/capability/PeopleCreateUsersCapability.php',
'PeopleDisableUsersCapability' => 'applications/people/capability/PeopleDisableUsersCapability.php',
'PeopleHovercardEngineExtension' => 'applications/people/engineextension/PeopleHovercardEngineExtension.php',
'PeopleMainMenuBarExtension' => 'applications/people/engineextension/PeopleMainMenuBarExtension.php',
'PeopleUserLogGarbageCollector' => 'applications/people/garbagecollector/PeopleUserLogGarbageCollector.php',
@ -5028,6 +5030,7 @@ phutil_register_library_map(array(
'PhrictionDocumentPHIDType' => 'applications/phriction/phid/PhrictionDocumentPHIDType.php',
'PhrictionDocumentPathHeraldField' => 'applications/phriction/herald/PhrictionDocumentPathHeraldField.php',
'PhrictionDocumentPolicyCodex' => 'applications/phriction/codex/PhrictionDocumentPolicyCodex.php',
'PhrictionDocumentPublishTransaction' => 'applications/phriction/xaction/PhrictionDocumentPublishTransaction.php',
'PhrictionDocumentQuery' => 'applications/phriction/query/PhrictionDocumentQuery.php',
'PhrictionDocumentSearchConduitAPIMethod' => 'applications/phriction/conduit/PhrictionDocumentSearchConduitAPIMethod.php',
'PhrictionDocumentSearchEngine' => 'applications/phriction/query/PhrictionDocumentSearchEngine.php',
@ -5035,6 +5038,7 @@ phutil_register_library_map(array(
'PhrictionDocumentTitleHeraldField' => 'applications/phriction/herald/PhrictionDocumentTitleHeraldField.php',
'PhrictionDocumentTitleTransaction' => 'applications/phriction/xaction/PhrictionDocumentTitleTransaction.php',
'PhrictionDocumentTransactionType' => 'applications/phriction/xaction/PhrictionDocumentTransactionType.php',
'PhrictionDocumentVersionTransaction' => 'applications/phriction/xaction/PhrictionDocumentVersionTransaction.php',
'PhrictionEditConduitAPIMethod' => 'applications/phriction/conduit/PhrictionEditConduitAPIMethod.php',
'PhrictionEditController' => 'applications/phriction/controller/PhrictionEditController.php',
'PhrictionHistoryConduitAPIMethod' => 'applications/phriction/conduit/PhrictionHistoryConduitAPIMethod.php',
@ -5044,6 +5048,7 @@ phutil_register_library_map(array(
'PhrictionMarkupPreviewController' => 'applications/phriction/controller/PhrictionMarkupPreviewController.php',
'PhrictionMoveController' => 'applications/phriction/controller/PhrictionMoveController.php',
'PhrictionNewController' => 'applications/phriction/controller/PhrictionNewController.php',
'PhrictionPublishController' => 'applications/phriction/controller/PhrictionPublishController.php',
'PhrictionRemarkupRule' => 'applications/phriction/markup/PhrictionRemarkupRule.php',
'PhrictionReplyHandler' => 'applications/phriction/mail/PhrictionReplyHandler.php',
'PhrictionSchemaSpec' => 'applications/phriction/storage/PhrictionSchemaSpec.php',
@ -6000,6 +6005,7 @@ phutil_register_library_map(array(
'DifferentialRevisionStatusTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionSummaryHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionSummaryTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionTestPlanHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionTestPlanTransaction' => 'DifferentialRevisionTransactionType',
'DifferentialRevisionTitleHeraldField' => 'DifferentialRevisionHeraldField',
'DifferentialRevisionTitleTransaction' => 'DifferentialRevisionTransactionType',
@ -7452,7 +7458,7 @@ phutil_register_library_map(array(
'PHUIDiffTableOfContentsListView' => 'AphrontView',
'PHUIDiffTwoUpInlineCommentRowScaffold' => 'PHUIDiffInlineCommentRowScaffold',
'PHUIDocumentSummaryView' => 'AphrontTagView',
'PHUIDocumentViewPro' => 'AphrontTagView',
'PHUIDocumentView' => 'AphrontTagView',
'PHUIFeedStoryExample' => 'PhabricatorUIExample',
'PHUIFeedStoryView' => 'AphrontView',
'PHUIFormDividerControl' => 'AphrontFormControl',
@ -7592,6 +7598,7 @@ phutil_register_library_map(array(
'PasteSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'PeopleBrowseUserDirectoryCapability' => 'PhabricatorPolicyCapability',
'PeopleCreateUsersCapability' => 'PhabricatorPolicyCapability',
'PeopleDisableUsersCapability' => 'PhabricatorPolicyCapability',
'PeopleHovercardEngineExtension' => 'PhabricatorHovercardEngineExtension',
'PeopleMainMenuBarExtension' => 'PhabricatorMainMenuBarExtension',
'PeopleUserLogGarbageCollector' => 'PhabricatorGarbageCollector',
@ -8288,10 +8295,7 @@ phutil_register_library_map(array(
'PhabricatorConfigManualActivity' => 'PhabricatorConfigEntryDAO',
'PhabricatorConfigModule' => 'Phobject',
'PhabricatorConfigModuleController' => 'PhabricatorConfigController',
'PhabricatorConfigOption' => array(
'Phobject',
'PhabricatorMarkupInterface',
),
'PhabricatorConfigOption' => 'Phobject',
'PhabricatorConfigOptionType' => 'Phobject',
'PhabricatorConfigPHIDModule' => 'PhabricatorConfigModule',
'PhabricatorConfigProxySource' => 'PhabricatorConfigSource',
@ -11130,27 +11134,29 @@ phutil_register_library_map(array(
),
'PhrictionDocumentAuthorHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentContentHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentContentTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentContentTransaction' => 'PhrictionDocumentVersionTransaction',
'PhrictionDocumentController' => 'PhrictionController',
'PhrictionDocumentDatasource' => 'PhabricatorTypeaheadDatasource',
'PhrictionDocumentDeleteTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentDeleteTransaction' => 'PhrictionDocumentVersionTransaction',
'PhrictionDocumentFerretEngine' => 'PhabricatorFerretEngine',
'PhrictionDocumentFulltextEngine' => 'PhabricatorFulltextEngine',
'PhrictionDocumentHeraldAdapter' => 'HeraldAdapter',
'PhrictionDocumentHeraldField' => 'HeraldField',
'PhrictionDocumentHeraldFieldGroup' => 'HeraldFieldGroup',
'PhrictionDocumentMoveAwayTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentMoveAwayTransaction' => 'PhrictionDocumentVersionTransaction',
'PhrictionDocumentMoveToTransaction' => 'PhrictionDocumentVersionTransaction',
'PhrictionDocumentPHIDType' => 'PhabricatorPHIDType',
'PhrictionDocumentPathHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentPolicyCodex' => 'PhabricatorPolicyCodex',
'PhrictionDocumentPublishTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhrictionDocumentSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'PhrictionDocumentSearchEngine' => 'PhabricatorApplicationSearchEngine',
'PhrictionDocumentStatus' => 'PhabricatorObjectStatus',
'PhrictionDocumentTitleHeraldField' => 'PhrictionDocumentHeraldField',
'PhrictionDocumentTitleTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionDocumentTitleTransaction' => 'PhrictionDocumentVersionTransaction',
'PhrictionDocumentTransactionType' => 'PhabricatorModularTransactionType',
'PhrictionDocumentVersionTransaction' => 'PhrictionDocumentTransactionType',
'PhrictionEditConduitAPIMethod' => 'PhrictionConduitAPIMethod',
'PhrictionEditController' => 'PhrictionController',
'PhrictionHistoryConduitAPIMethod' => 'PhrictionConduitAPIMethod',
@ -11160,6 +11166,7 @@ phutil_register_library_map(array(
'PhrictionMarkupPreviewController' => 'PhabricatorController',
'PhrictionMoveController' => 'PhrictionController',
'PhrictionNewController' => 'PhrictionController',
'PhrictionPublishController' => 'PhrictionController',
'PhrictionRemarkupRule' => 'PhutilRemarkupRule',
'PhrictionReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
'PhrictionSchemaSpec' => 'PhabricatorConfigSchemaSpec',

View file

@ -153,30 +153,16 @@ final class PhabricatorConfigEditController
$e_value);
}
$engine = new PhabricatorMarkupEngine();
$engine->setViewer($viewer);
$engine->addObject($option, 'description');
$engine->process();
$description = phutil_tag(
'div',
array(
'class' => 'phabricator-remarkup',
),
$engine->getOutput($option, 'description'));
$form
->setUser($viewer)
->addHiddenInput('issue', $request->getStr('issue'));
$description = $option->getDescription();
if (strlen($description)) {
$description_view = new PHUIRemarkupView($viewer, $description);
$form
->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Description'))
->setValue($description_view));
$description = $option->newDescriptionRemarkupView($viewer);
if ($description) {
$form->appendChild(
id(new AphrontFormMarkupControl())
->setLabel(pht('Description'))
->setValue($description));
}
if ($group) {

View file

@ -60,17 +60,10 @@ final class PhabricatorConfigGroupController
$db_values = mpull($db_values, null, 'getConfigKey');
}
$engine = id(new PhabricatorMarkupEngine())
->setViewer($this->getRequest()->getUser());
foreach ($options as $option) {
$engine->addObject($option, 'summary');
}
$engine->process();
$list = new PHUIObjectItemListView();
$list->setBig(true);
foreach ($options as $option) {
$summary = $engine->getOutput($option, 'summary');
$summary = $option->getSummary();
$item = id(new PHUIObjectItemView())
->setHeader($option->getKey())

View file

@ -33,27 +33,26 @@ final class PhabricatorConfigIssueListController
PhabricatorSetupCheck::GROUP_OTHER,
'fa-question-circle');
$no_issues = null;
if (empty($issues)) {
$no_issues = id(new PHUIInfoView())
$title = pht('Setup Issues');
$header = $this->buildHeaderView($title);
if (!$issues) {
$issue_list = id(new PHUIInfoView())
->setTitle(pht('No Issues'))
->appendChild(
pht('Your install has no current setup issues to resolve.'))
->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
} else {
$issue_list = array(
$important,
$php,
$mysql,
$other,
);
$issue_list = $this->buildConfigBoxView(pht('Issues'), $issue_list);
}
$title = pht('Setup Issues');
$header = $this->buildHeaderView($title);
$issue_list = array(
$important,
$php,
$mysql,
$other,
);
$issue_list = $this->buildConfigBoxView(pht('Issues'), $issue_list);
$crumbs = $this->buildApplicationCrumbs()
->addTextCrumb($title)
->setBorder(true);
@ -62,10 +61,7 @@ final class PhabricatorConfigIssueListController
->setHeader($header)
->setNavigation($nav)
->setFixed(true)
->setMainColumn(array(
$no_issues,
$issue_list,
));
->setMainColumn($issue_list);
return $this->newPage()
->setTitle($title)

View file

@ -1,8 +1,7 @@
<?php
final class PhabricatorConfigOption
extends Phobject
implements PhabricatorMarkupInterface {
extends Phobject {
private $key;
private $default;
@ -204,43 +203,19 @@ final class PhabricatorConfigOption
return $this;
}
/* -( PhabricatorMarkupInterface )----------------------------------------- */
public function getMarkupFieldKey($field) {
return $this->getKey().':'.$field;
}
public function newMarkupEngine($field) {
return PhabricatorMarkupEngine::newMarkupEngine(array());
}
public function getMarkupText($field) {
switch ($field) {
case 'description':
$text = $this->getDescription();
break;
case 'summary':
$text = $this->getSummary();
break;
public function newDescriptionRemarkupView(PhabricatorUser $viewer) {
$description = $this->getDescription();
if (!strlen($description)) {
return null;
}
// TODO: We should probably implement this as a real Markup rule, but
// markup rules are a bit of a mess right now and it doesn't hurt us to
// fake this.
$text = preg_replace(
// TODO: Some day, we should probably implement this as a real rule.
$description = preg_replace(
'/{{([^}]+)}}/',
'[[/config/edit/\\1/ | \\1]]',
$text);
$description);
return $text;
}
public function didMarkupText($field, $output, PhutilMarkupEngine $engine) {
return $output;
}
public function shouldUseMarkupCache($field) {
return false;
return new PHUIRemarkupView($viewer, $description);
}
}

View file

@ -146,49 +146,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
return $xactions;
}
protected function requireCapabilities(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
parent::requireCapabilities($object, $xaction);
switch ($xaction->getTransactionType()) {
case ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE:
$old_map = array_fuse($xaction->getOldValue());
$new_map = array_fuse($xaction->getNewValue());
$add = array_keys(array_diff_key($new_map, $old_map));
$rem = array_keys(array_diff_key($old_map, $new_map));
$actor_phid = $this->getActingAsPHID();
$is_join = (($add === array($actor_phid)) && !$rem);
$is_leave = (($rem === array($actor_phid)) && !$add);
if ($is_join) {
// Anyone can join a thread they can see.
} else if ($is_leave) {
// Anyone can leave a thread.
} else {
// You need CAN_EDIT to add or remove participants. For additional
// discussion, see D17696 and T4411.
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
}
break;
case ConpherenceThreadTitleTransaction::TRANSACTIONTYPE:
case ConpherenceThreadTopicTransaction::TRANSACTIONTYPE:
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
break;
}
}
protected function shouldSendMail(
PhabricatorLiskDAO $object,
array $xactions) {

View file

@ -114,4 +114,34 @@ final class ConpherenceThreadParticipantsTransaction
return $errors;
}
public function getRequiredCapabilities(
$object,
PhabricatorApplicationTransaction $xaction) {
$old_map = array_fuse($xaction->getOldValue());
$new_map = array_fuse($xaction->getNewValue());
$add = array_keys(array_diff_key($new_map, $old_map));
$rem = array_keys(array_diff_key($old_map, $new_map));
$actor_phid = $this->getActingAsPHID();
$is_join = (($add === array($actor_phid)) && !$rem);
$is_leave = (($rem === array($actor_phid)) && !$add);
if ($is_join) {
// Anyone can join a thread they can see.
return null;
}
if ($is_leave) {
// Anyone can leave a thread.
return null;
}
// You need CAN_EDIT to add or remove participants. For additional
// discussion, see D17696 and T4411.
return PhabricatorPolicyCapability::CAN_EDIT;
}
}

View file

@ -1315,7 +1315,7 @@ final class DifferentialRevisionViewController
}
return id(new HarbormasterUnitSummaryView())
->setUser($viewer)
->setViewer($viewer)
->setExcuse($excuse)
->setBuildable($diff->getBuildable())
->setUnitMessages($diff->getUnitMessages())

View file

@ -54,4 +54,28 @@ final class DifferentialBuildableEngine
$this->applyTransactions(array($xaction));
}
public function getAuthorIdentity() {
$object = $this->getObject();
if ($object instanceof DifferentialRevision) {
$object = $object->loadActiveDiff();
}
$authorship = $object->getDiffAuthorshipDict();
if (!isset($authorship['authorName'])) {
return null;
}
$name = $authorship['authorName'];
$address = idx($authorship, 'authorEmail');
$full = id(new PhutilEmailAddress())
->setDisplayName($name)
->setAddress($address);
return id(new PhabricatorRepositoryIdentity())
->setIdentityName((string)$full)
->makeEphemeral();
}
}

View file

@ -10,10 +10,7 @@ final class DifferentialRevisionSummaryHeraldField
}
public function getHeraldFieldValue($object) {
// NOTE: For historical reasons, this field includes the test plan. We
// could maybe try to fix this some day, but it probably aligns reasonably
// well with user expectation without harming anything.
return $object->getSummary()."\n\n".$object->getTestPlan();
return $object->getSummary();
}
protected function getHeraldFieldStandardType() {

View file

@ -0,0 +1,20 @@
<?php
final class DifferentialRevisionTestPlanHeraldField
extends DifferentialRevisionHeraldField {
const FIELDCONST = 'differential.revision.test-plan';
public function getHeraldFieldName() {
return pht('Revision test plan');
}
public function getHeraldFieldValue($object) {
return $object->getTestPlan();
}
protected function getHeraldFieldStandardType() {
return self::STANDARD_TEXT;
}
}

View file

@ -34,4 +34,10 @@ final class DiffusionBuildableEngine
$this->applyTransactions(array($xaction));
}
public function getAuthorIdentity() {
return $this->getObject()
->loadIdentities($this->getViewer())
->getAuthorIdentity();
}
}

View file

@ -94,7 +94,7 @@ final class DiffusionReadmeView extends DiffusionView {
}
$readme_content = phutil_tag_div($class, $readme_content);
$document = id(new PHUIDocumentViewPro())
$document = id(new PHUIDocumentView())
->setFluid(true)
->appendChild($readme_content);

View file

@ -72,7 +72,7 @@ final class DivinerAtomController extends DivinerController {
$prop_list = new PHUIPropertyGroupView();
$prop_list->addPropertyList($properties);
$document = id(new PHUIDocumentViewPro())
$document = id(new PHUIDocumentView())
->setBook($book->getTitle(), $group_name)
->setHeader($header)
->addClass('diviner-view');

View file

@ -45,7 +45,7 @@ final class DivinerBookController extends DivinerController {
->setName($book->getRepository()->getMonogram()));
}
$document = new PHUIDocumentViewPro();
$document = new PHUIDocumentView();
$document->setHeader($header);
$document->addClass('diviner-view');

View file

@ -27,7 +27,7 @@ final class DivinerMainController extends DivinerController {
->setHeader(pht('Documentation Books'))
->addActionLink($query_button);
$document = new PHUIDocumentViewPro();
$document = new PHUIDocumentView();
$document->setHeader($header);
$document->addClass('diviner-view');

View file

@ -163,6 +163,30 @@ final class DrydockLeaseViewController extends DrydockLeaseController {
}
$view->addProperty(pht('Expires'), $until_display);
$acquired_epoch = $lease->getAcquiredEpoch();
$activated_epoch = $lease->getActivatedEpoch();
if ($acquired_epoch) {
$acquired_display = phabricator_datetime($acquired_epoch, $viewer);
} else {
if ($activated_epoch) {
$acquired_display = phutil_tag(
'em',
array(),
pht('Activated on Acquisition'));
} else {
$acquired_display = phutil_tag('em', array(), pht('Not Acquired'));
}
}
$view->addProperty(pht('Acquired'), $acquired_display);
if ($activated_epoch) {
$activated_display = phabricator_datetime($activated_epoch, $viewer);
} else {
$activated_display = phutil_tag('em', array(), pht('Not Activated'));
}
$view->addProperty(pht('Activated'), $activated_display);
$attributes = $lease->getAttributes();
if ($attributes) {
$view->addSectionHeader(

View file

@ -10,6 +10,8 @@ final class DrydockLease extends DrydockDAO
protected $authorizingPHID;
protected $attributes = array();
protected $status = DrydockLeaseStatus::STATUS_PENDING;
protected $acquiredEpoch;
protected $activatedEpoch;
private $resource = self::ATTACHABLE;
private $unconsumedCommands = self::ATTACHABLE;
@ -62,6 +64,22 @@ final class DrydockLease extends DrydockDAO
$this->scheduleUpdate();
}
public function setStatus($status) {
if ($status == DrydockLeaseStatus::STATUS_ACQUIRED) {
if (!$this->getAcquiredEpoch()) {
$this->setAcquiredEpoch(PhabricatorTime::getNow());
}
}
if ($status == DrydockLeaseStatus::STATUS_ACTIVE) {
if (!$this->getActivatedEpoch()) {
$this->setActivatedEpoch(PhabricatorTime::getNow());
}
}
return parent::setStatus($status);
}
public function getLeaseName() {
return pht('Lease %d', $this->getID());
}
@ -78,6 +96,8 @@ final class DrydockLease extends DrydockDAO
'resourceType' => 'text128',
'ownerPHID' => 'phid?',
'resourcePHID' => 'phid?',
'acquiredEpoch' => 'epoch?',
'activatedEpoch' => 'epoch?',
),
self::CONFIG_KEY_SCHEMA => array(
'key_resource' => array(

View file

@ -169,7 +169,7 @@ final class PhabricatorGuideInstallModule extends PhabricatorGuideModule {
$intro = new PHUIRemarkupView($viewer, $intro);
$intro = id(new PHUIDocumentViewPro())
$intro = id(new PHUIDocumentView())
->appendChild($intro);
return array($intro, $guide_items);

View file

@ -206,7 +206,7 @@ final class PhabricatorGuideQuickStartModule extends PhabricatorGuideModule {
'these features at your own pace.');
$intro = new PHUIRemarkupView($viewer, $intro);
$intro = id(new PHUIDocumentViewPro())
$intro = id(new PHUIDocumentView())
->appendChild($intro);
return array($intro, $guide_items);

View file

@ -318,7 +318,7 @@ final class HarbormasterBuildableViewController
if ($lint_data) {
$lint_table = id(new HarbormasterLintPropertyView())
->setUser($viewer)
->setViewer($viewer)
->setLimit(10)
->setLintMessages($lint_data);
@ -343,6 +343,7 @@ final class HarbormasterBuildableViewController
if ($unit_data) {
$unit = id(new HarbormasterUnitSummaryView())
->setViewer($viewer)
->setBuildable($buildable)
->setUnitMessages($unit_data)
->setShowViewAll(true)

View file

@ -39,6 +39,7 @@ final class HarbormasterUnitMessageListController
}
$unit = id(new HarbormasterUnitSummaryView())
->setViewer($viewer)
->setBuildable($buildable)
->setUnitMessages($unit_data);

View file

@ -88,23 +88,7 @@ final class HarbormasterUnitMessageViewController
pht('Run At'),
phabricator_datetime($message->getDateCreated(), $viewer));
$details = $message->getUnitMessageDetails();
if (strlen($details)) {
// TODO: Use the log view here, once it gets cleaned up.
// Shenanigans below.
$details = phutil_tag(
'div',
array(
'class' => 'PhabricatorMonospaced',
'style' =>
'white-space: pre-wrap; '.
'color: #666666; '.
'overflow-x: auto;',
),
$details);
} else {
$details = phutil_tag('em', array(), pht('No details provided.'));
}
$details = $message->newUnitMessageDetailsView($viewer);
$view->addSectionHeader(
pht('Details'),

View file

@ -101,5 +101,8 @@ abstract class HarbormasterBuildableEngine
$xactions);
}
public function getAuthorIdentity() {
return null;
}
}

View file

@ -117,6 +117,20 @@ EOTEXT
),
);
$engine = HarbormasterBuildableEngine::newForObject(
$object,
$viewer);
$author_identity = $engine->getAuthorIdentity();
if ($author_identity) {
$data_structure += array(
'author' => array(
'name' => $author_identity->getIdentityDisplayName(),
'email' => $author_identity->getIdentityEmailAddress(),
),
);
}
$json_data = phutil_json_encode($data_structure);
$credential_phid = $this->getSetting('token');

View file

@ -13,6 +13,9 @@ final class HarbormasterBuildUnitMessage
private $buildTarget = self::ATTACHABLE;
const FORMAT_TEXT = 'text';
const FORMAT_REMARKUP = 'remarkup';
public static function initializeNewUnitMessage(
HarbormasterBuildTarget $build_target) {
return id(new HarbormasterBuildUnitMessage())
@ -66,6 +69,13 @@ final class HarbormasterBuildUnitMessage
'description' => pht(
'Additional human-readable information about the failure.'),
),
'format' => array(
'type' => 'optional string',
'description' => pht(
'Format for the text provided in "details". Valid values are '.
'"text" (default) or "remarkup". This controls how test details '.
'are rendered when shown to users.'),
),
);
}
@ -104,6 +114,11 @@ final class HarbormasterBuildUnitMessage
$obj->setProperty('details', $details);
}
$format = idx($dict, 'format');
if ($format) {
$obj->setProperty('format', $format);
}
return $obj;
}
@ -149,6 +164,66 @@ final class HarbormasterBuildUnitMessage
return $this->getProperty('details', '');
}
public function getUnitMessageDetailsFormat() {
return $this->getProperty('format', self::FORMAT_TEXT);
}
public function newUnitMessageDetailsView(
PhabricatorUser $viewer,
$summarize = false) {
$format = $this->getUnitMessageDetailsFormat();
$is_text = ($format !== self::FORMAT_REMARKUP);
$is_remarkup = ($format === self::FORMAT_REMARKUP);
$full_details = $this->getUnitMessageDetails();
if (!strlen($full_details)) {
if ($summarize) {
return null;
}
$details = phutil_tag('em', array(), pht('No details provided.'));
} else if ($summarize) {
if ($is_text) {
$details = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(2048)
->truncateString($full_details);
$details = phutil_split_lines($details);
$limit = 3;
if (count($details) > $limit) {
$details = array_slice($details, 0, $limit);
}
$details = implode('', $details);
} else {
$details = $full_details;
}
} else {
$details = $full_details;
}
require_celerity_resource('harbormaster-css');
$classes = array();
$classes[] = 'harbormaster-unit-details';
if ($is_remarkup) {
$details = new PHUIRemarkupView($viewer, $details);
} else {
$classes[] = 'harbormaster-unit-details-text';
$classes[] = 'PhabricatorMonospaced';
}
return phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
$details);
}
public function getUnitMessageDisplayName() {
$name = $this->getName();

View file

@ -34,9 +34,8 @@ final class HarbormasterUnitPropertyView extends AphrontView {
return $this;
}
public function render() {
require_celerity_resource('harbormaster-css');
$viewer = $this->getViewer();
$messages = $this->unitMessages;
$messages = msort($messages, 'getSortKey');
@ -84,13 +83,10 @@ final class HarbormasterUnitPropertyView extends AphrontView {
$name);
}
$details = $message->getUnitMessageDetails();
if (strlen($details)) {
$name = array(
$name,
$this->renderUnitTestDetails($details),
);
}
$name = array(
$name,
$message->newUnitMessageDetailsView($viewer, true),
);
$rows[] = array(
$result_icon,
@ -158,25 +154,4 @@ final class HarbormasterUnitPropertyView extends AphrontView {
return $table;
}
private function renderUnitTestDetails($full_details) {
$details = id(new PhutilUTF8StringTruncator())
->setMaximumBytes(2048)
->truncateString($full_details);
$details = phutil_split_lines($details);
$limit = 3;
if (count($details) > $limit) {
$details = array_slice($details, 0, $limit);
}
$details = implode('', $details);
return phutil_tag(
'div',
array(
'class' => 'PhabricatorMonospaced harbormaster-unit-details',
),
$details);
}
}

View file

@ -77,6 +77,7 @@ final class HarbormasterUnitSummaryView extends AphrontView {
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY);
$table = id(new HarbormasterUnitPropertyView())
->setViewer($this->getViewer())
->setUnitMessages($messages);
if ($this->showViewAll) {

View file

@ -272,7 +272,7 @@ final class LegalpadDocumentSignController extends LegalpadController {
$preamble_box->addPropertyList($preamble);
}
$content = id(new PHUIDocumentViewPro())
$content = id(new PHUIDocumentView())
->addClass('legalpad')
->setHeader($header)
->appendChild(

View file

@ -132,7 +132,7 @@ final class PhabricatorApplicationEmailCommandsController
$header = id(new PHUIHeaderView())
->setHeader($title);
$document = id(new PHUIDocumentViewPro())
$document = id(new PHUIDocumentView())
->setHeader($header)
->appendChild($info_view)
->appendChild($content_box);

View file

@ -97,6 +97,9 @@ final class PhabricatorPeopleApplication extends PhabricatorApplication {
PeopleCreateUsersCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
PeopleDisableUsersCapability::CAPABILITY => array(
'default' => PhabricatorPolicies::POLICY_ADMIN,
),
PeopleBrowseUserDirectoryCapability::CAPABILITY => array(),
);
}

View file

@ -0,0 +1,16 @@
<?php
final class PeopleDisableUsersCapability
extends PhabricatorPolicyCapability {
const CAPABILITY = 'people.disable.users';
public function getCapabilityName() {
return pht('Can Disable Users');
}
public function describeCapabilityRejection() {
return pht('You do not have permission to disable or enable users.');
}
}

View file

@ -10,6 +10,14 @@ final class UserDisableConduitAPIMethod extends UserConduitAPIMethod {
return pht('Permanently disable specified users (admin only).');
}
public function getMethodStatus() {
return self::METHOD_STATUS_DEPRECATED;
}
public function getMethodStatusDescription() {
return pht('Obsoleted by method "user.edit".');
}
protected function defineParamTypes() {
return array(
'phids' => 'required list<phid>',
@ -43,11 +51,23 @@ final class UserDisableConduitAPIMethod extends UserConduitAPIMethod {
throw new ConduitException('ERR-BAD-PHID');
}
foreach ($users as $user) {
id(new PhabricatorUserEditor())
->setActor($actor)
->disableUser($user, true);
foreach ($phids as $phid) {
$params = array(
'transactions' => array(
array(
'type' => 'disabled',
'value' => true,
),
),
'objectIdentifier' => $phid,
);
id(new ConduitCall('user.edit', $params))
->setUser($actor)
->execute();
}
return null;
}
}

View file

@ -10,6 +10,14 @@ final class UserEnableConduitAPIMethod extends UserConduitAPIMethod {
return pht('Re-enable specified users (admin only).');
}
public function getMethodStatus() {
return self::METHOD_STATUS_DEPRECATED;
}
public function getMethodStatusDescription() {
return pht('Obsoleted by method "user.edit".');
}
protected function defineParamTypes() {
return array(
'phids' => 'required list<phid>',
@ -43,11 +51,23 @@ final class UserEnableConduitAPIMethod extends UserConduitAPIMethod {
throw new ConduitException('ERR-BAD-PHID');
}
foreach ($users as $user) {
id(new PhabricatorUserEditor())
->setActor($actor)
->disableUser($user, false);
foreach ($phids as $phid) {
$params = array(
'transactions' => array(
array(
'type' => 'disabled',
'value' => false,
),
),
'objectIdentifier' => $phid,
);
id(new ConduitCall('user.edit', $params))
->setUser($actor)
->execute();
}
return null;
}
}

View file

@ -16,6 +16,13 @@ final class PhabricatorPeopleApproveController
$done_uri = $this->getApplicationURI('query/approval/');
if ($user->getIsApproved()) {
return $this->newDialog()
->setTitle(pht('Already Approved'))
->appendChild(pht('This user has already been approved.'))
->addCancelButton($done_uri);
}
if ($request->isFormPost()) {
id(new PhabricatorUserEditor())
->setActor($viewer)

View file

@ -3,10 +3,14 @@
final class PhabricatorPeopleDisableController
extends PhabricatorPeopleController {
public function shouldRequireAdmin() {
return false;
}
public function handleRequest(AphrontRequest $request) {
$viewer = $this->getViewer();
$id = $request->getURIData('id');
$via = $request->getURIData('id');
$via = $request->getURIData('via');
$user = id(new PhabricatorPeopleQuery())
->setViewer($viewer)
@ -20,11 +24,36 @@ final class PhabricatorPeopleDisableController
// on profiles and also via the "X" action on the approval queue. We do
// things slightly differently depending on the context the actor is in.
// In particular, disabling via "Disapprove" requires you be an
// administrator (and bypasses the "Can Disable Users" permission).
// Disabling via "Disable" requires the permission only.
$is_disapprove = ($via == 'disapprove');
if ($is_disapprove) {
$done_uri = $this->getApplicationURI('query/approval/');
if (!$viewer->getIsAdmin()) {
return $this->newDialog()
->setTitle(pht('No Permission'))
->appendParagraph(pht('Only administrators can disapprove users.'))
->addCancelButton($done_uri);
}
if ($user->getIsApproved()) {
return $this->newDialog()
->setTitle(pht('Already Approved'))
->appendParagraph(pht('This user has already been approved.'))
->addCancelButton($done_uri);
}
// On the "Disapprove" flow, bypass the "Can Disable Users" permission.
$actor = PhabricatorUser::getOmnipotentUser();
$should_disable = true;
} else {
$this->requireApplicationCapability(
PeopleDisableUsersCapability::CAPABILITY);
$actor = $viewer;
$done_uri = $this->getApplicationURI("manage/{$id}/");
$should_disable = !$user->getIsDisabled();
}
@ -39,9 +68,19 @@ final class PhabricatorPeopleDisableController
}
if ($request->isFormPost()) {
id(new PhabricatorUserEditor())
->setActor($viewer)
->disableUser($user, $should_disable);
$xactions = array();
$xactions[] = id(new PhabricatorUserTransaction())
->setTransactionType(PhabricatorUserDisableTransaction::TRANSACTIONTYPE)
->setNewValue($should_disable);
id(new PhabricatorUserTransactionEditor())
->setActor($actor)
->setActingAsPHID($viewer->getPHID())
->setContentSourceFromRequest($request)
->setContinueOnMissingFields(true)
->setContinueOnNoEffect(true)
->applyTransactions($user, $xactions);
return id(new AphrontRedirectResponse())->setURI($done_uri);
}

View file

@ -75,11 +75,22 @@ final class PhabricatorPeopleProfileManageController
private function buildCurtain(PhabricatorUser $user) {
$viewer = $this->getViewer();
$is_self = ($user->getPHID() === $viewer->getPHID());
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$user,
PhabricatorPolicyCapability::CAN_EDIT);
$is_admin = $viewer->getIsAdmin();
$can_admin = ($is_admin && !$is_self);
$has_disable = $this->hasApplicationCapability(
PeopleDisableUsersCapability::CAPABILITY);
$can_disable = ($has_disable && !$is_self);
$can_welcome = ($is_admin && $user->canEstablishWebSessions());
$curtain = $this->newCurtainView($user);
$curtain->addAction(
@ -114,10 +125,6 @@ final class PhabricatorPeopleProfileManageController
$empower_name = pht('Make Administrator');
}
$is_admin = $viewer->getIsAdmin();
$is_self = ($user->getPHID() === $viewer->getPHID());
$can_admin = ($is_admin && !$is_self);
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon($empower_icon)
@ -146,7 +153,7 @@ final class PhabricatorPeopleProfileManageController
id(new PhabricatorActionView())
->setIcon($disable_icon)
->setName($disable_name)
->setDisabled(!$can_admin)
->setDisabled(!$can_disable)
->setWorkflow(true)
->setHref($this->getApplicationURI('disable/'.$user->getID().'/')));
@ -158,8 +165,6 @@ final class PhabricatorPeopleProfileManageController
->setWorkflow(true)
->setHref($this->getApplicationURI('delete/'.$user->getID().'/')));
$can_welcome = ($is_admin && $user->canEstablishWebSessions());
$curtain->addAction(
id(new PhabricatorActionView())
->setIcon('fa-envelope')

View file

@ -293,45 +293,6 @@ final class PhabricatorUserEditor extends PhabricatorEditor {
return $this;
}
/**
* @task role
*/
public function disableUser(PhabricatorUser $user, $disable) {
$actor = $this->requireActor();
if (!$user->getID()) {
throw new Exception(pht('User has not been created yet!'));
}
$user->openTransaction();
$user->beginWriteLocking();
$user->reload();
if ($user->getIsDisabled() == $disable) {
$user->endWriteLocking();
$user->killTransaction();
return $this;
}
$log = PhabricatorUserLog::initializeNewLog(
$actor,
$user->getPHID(),
PhabricatorUserLog::ACTION_DISABLE);
$log->setOldValue($user->getIsDisabled());
$log->setNewValue($disable);
$user->setIsDisabled((int)$disable);
$user->save();
$log->save();
$user->endWriteLocking();
$user->saveTransaction();
return $this;
}
/**
* @task role
*/

View file

@ -60,6 +60,10 @@ final class PhabricatorUserDisableTransaction
continue;
}
// You must have the "Can Disable Users" permission to disable a user.
$this->requireApplicationCapability(
PeopleDisableUsersCapability::CAPABILITY);
if ($this->getActingAsPHID() === $object->getPHID()) {
$errors[] = $this->newInvalidError(
pht('You can not enable or disable your own account.'));
@ -69,4 +73,14 @@ final class PhabricatorUserDisableTransaction
return $errors;
}
public function getRequiredCapabilities(
$object,
PhabricatorApplicationTransaction $xaction) {
// You do not need to be able to edit users to disable them. Instead, this
// requirement is replaced with a requirement that you have the "Can
// Disable Users" permission.
return null;
}
}

View file

@ -84,7 +84,7 @@ final class PhameHomeController extends PhamePostController {
pht('Recent Posts'),
$this->getApplicationURI('post/'));
$page = id(new PHUIDocumentViewPro())
$page = id(new PHUIDocumentView())
->setHeader($header)
->appendChild($post_list);

View file

@ -85,7 +85,7 @@ final class PhameBlogViewController extends PhameLiveController {
}
}
$page = id(new PHUIDocumentViewPro())
$page = id(new PHUIDocumentView())
->setHeader($header)
->appendChild($post_list);

View file

@ -30,7 +30,7 @@ final class PhamePostViewController
$header->setActionList($actions);
}
$document = id(new PHUIDocumentViewPro())
$document = id(new PHUIDocumentView())
->setHeader($header);
if ($moved) {

View file

@ -56,6 +56,8 @@ final class PhabricatorPhrictionApplication extends PhabricatorApplication {
'edit/(?:(?P<id>[1-9]\d*)/)?' => 'PhrictionEditController',
'delete/(?P<id>[1-9]\d*)/' => 'PhrictionDeleteController',
'publish/(?P<documentID>[1-9]\d*)/(?P<contentID>[1-9]\d*)/'
=> 'PhrictionPublishController',
'new/' => 'PhrictionNewController',
'move/(?P<id>[1-9]\d*)/' => 'PhrictionMoveController',

View file

@ -65,8 +65,8 @@ final class PhrictionDiffController extends PhrictionController {
$slug = $document->getSlug();
$revert_l = $this->renderRevertButton($content_l, $current);
$revert_r = $this->renderRevertButton($content_r, $current);
$revert_l = $this->renderRevertButton($document, $content_l, $current);
$revert_r = $this->renderRevertButton($document, $content_r, $current);
$crumbs = $this->buildApplicationCrumbs();
$crumb_views = $this->renderBreadcrumbs($slug);
@ -179,10 +179,11 @@ final class PhrictionDiffController extends PhrictionController {
}
private function renderRevertButton(
PhrictionDocument $document,
PhrictionContent $content,
PhrictionContent $current) {
$document_id = $content->getDocumentID();
$document_id = $document->getID();
$version = $content->getVersion();
$hidden_statuses = array(

View file

@ -20,8 +20,6 @@ final class PhrictionDocumentController
return id(new AphrontRedirectResponse())->setURI($uri);
}
require_celerity_resource('phriction-document-css');
$version_note = null;
$core_content = '';
$move_notice = '';
@ -29,6 +27,8 @@ final class PhrictionDocumentController
$content = null;
$toc = null;
$is_draft = false;
$document = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withSlugs(array($slug))
@ -66,8 +66,9 @@ final class PhrictionDocumentController
->addAction($create_button);
} else {
$version = $request->getInt('v');
$max_version = (int)$document->getMaxVersion();
$version = $request->getInt('v');
if ($version) {
$content = id(new PhrictionContentQuery())
->setViewer($viewer)
@ -78,15 +79,111 @@ final class PhrictionDocumentController
return new Aphront404Response();
}
if ($content->getID() != $document->getContentID()) {
$version_note = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild(
pht(
'You are viewing an older version of this document, as it '.
'appeared on %s.',
phabricator_datetime($content->getDateCreated(), $viewer)));
// When the "v" parameter exists, the user is in history mode so we
// show this header even if they're looking at the current version
// of the document. This keeps the next/previous links working.
$view_version = (int)$content->getVersion();
$published_version = (int)$document->getContent()->getVersion();
if ($view_version < $published_version) {
$version_note = pht(
'You are viewing an older version of this document, as it '.
'appeared on %s.',
phabricator_datetime($content->getDateCreated(), $viewer));
} else if ($view_version > $published_version) {
$is_draft = true;
$version_note = pht(
'You are viewing an unpublished draft of this document.');
} else {
$version_note = pht(
'You are viewing the current published version of this document.');
}
$version_note = array(
phutil_tag(
'strong',
array(),
pht('Version %d of %d: ', $view_version, $max_version)),
' ',
$version_note,
);
$version_note = id(new PHUIInfoView())
->setSeverity(PHUIInfoView::SEVERITY_NOTICE)
->appendChild($version_note);
$document_uri = new PhutilURI($document->getURI());
if ($view_version > 1) {
$previous_uri = $document_uri->alter('v', ($view_version - 1));
} else {
$previous_uri = null;
}
if ($view_version !== $published_version) {
$current_uri = $document_uri->alter('v', $published_version);
} else {
$current_uri = null;
}
if ($view_version < $max_version) {
$next_uri = $document_uri->alter('v', ($view_version + 1));
} else {
$next_uri = null;
}
if ($view_version !== $max_version) {
$draft_uri = $document_uri->alter('v', $max_version);
} else {
$draft_uri = null;
}
$button_bar = id(new PHUIButtonBarView())
->addButton(
id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-backward')
->setDisabled(!$previous_uri)
->setHref($previous_uri)
->setText(pht('Previous')))
->addButton(
id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-file-o')
->setDisabled(!$current_uri)
->setHref($current_uri)
->setText(pht('Published')))
->addButton(
id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-forward', false)
->setDisabled(!$next_uri)
->setHref($next_uri)
->setText(pht('Next')))
->addButton(
id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-fast-forward', false)
->setDisabled(!$draft_uri)
->setHref($draft_uri)
->setText(pht('Draft')));
require_celerity_resource('phui-document-view-css');
$version_note = array(
$version_note,
phutil_tag(
'div',
array(
'class' => 'phui-document-version-navigation',
),
$button_bar),
);
} else {
$content = $document->getContent();
}
@ -201,7 +298,10 @@ final class PhrictionDocumentController
$children = $this->renderDocumentChildren($slug);
$actions = $this->buildActionView($viewer, $document);
$curtain = null;
if ($document->getID()) {
$curtain = $this->buildCurtain($document, $content);
}
$crumbs = $this->buildApplicationCrumbs();
$crumbs->setBorder(true);
@ -213,10 +313,17 @@ final class PhrictionDocumentController
$header = id(new PHUIHeaderView())
->setUser($viewer)
->setPolicyObject($document)
->setHeader($page_title)
->setActionList($actions);
->setHeader($page_title);
if ($content) {
if ($is_draft) {
$draft_tag = id(new PHUITagView())
->setName(pht('Draft'))
->setIcon('fa-spinner')
->setColor('pink')
->setType(PHUITagView::TYPE_SHADE);
$header->addTag($draft_tag);
} else if ($content) {
$header->setEpoch($content->getDateCreated());
}
@ -227,25 +334,30 @@ final class PhrictionDocumentController
}
$prop_list = phutil_tag_div('phui-document-view-pro-box', $prop_list);
$page_content = id(new PHUIDocumentViewPro())
$page_content = id(new PHUIDocumentView())
->setBanner($version_note)
->setHeader($header)
->setToc($toc)
->appendChild(
array(
$version_note,
$move_notice,
$core_content,
));
if ($curtain) {
$page_content->setCurtain($curtain);
}
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->setPageObjectPHIDs(array($document->getPHID()))
->appendChild(array(
$page_content,
$prop_list,
$children,
));
->appendChild(
array(
$page_content,
$prop_list,
$children,
));
}
@ -257,8 +369,7 @@ final class PhrictionDocumentController
$viewer = $this->getViewer();
$view = id(new PHUIPropertyListView())
->setUser($viewer)
->setObject($document);
->setUser($viewer);
$view->addProperty(
pht('Last Author'),
@ -271,37 +382,70 @@ final class PhrictionDocumentController
return $view;
}
private function buildActionView(
PhabricatorUser $viewer,
PhrictionDocument $document) {
private function buildCurtain(
PhrictionDocument $document,
PhrictionContent $content) {
$viewer = $this->getViewer();
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$document,
PhabricatorPolicyCapability::CAN_EDIT);
$slug = PhabricatorSlug::normalize($this->slug);
$id = $document->getID();
$action_view = id(new PhabricatorActionListView())
->setUser($viewer)
->setObject($document);
$curtain = $this->newCurtainView($document);
if (!$document->getID()) {
return $action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('Create This Document'))
->setIcon('fa-plus-square')
->setHref('/phriction/edit/?slug='.$slug));
}
$action_view->addAction(
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Edit Document'))
->setDisabled(!$can_edit)
->setIcon('fa-pencil')
->setHref('/phriction/edit/'.$document->getID().'/'));
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setIcon('fa-history')
->setHref(PhrictionDocument::getSlugURI($slug, 'history')));
$is_current = false;
$content_id = null;
$is_draft = false;
if ($content) {
if ($content->getPHID() == $document->getContentPHID()) {
$is_current = true;
}
$content_id = $content->getID();
$current_version = $document->getContent()->getVersion();
$is_draft = ($content->getVersion() >= $current_version);
}
$can_publish = ($can_edit && $content && !$is_current);
if ($is_draft) {
$publish_name = pht('Publish Draft');
} else {
$publish_name = pht('Publish Revert');
}
$publish_uri = "/phriction/publish/{$id}/{$content_id}/";
if (PhabricatorEnv::getEnvConfig('phabricator.show-prototypes')) {
$publish_name = pht('Publish (Prototype!)');
$curtain->addAction(
id(new PhabricatorActionView())
->setName($publish_name)
->setIcon('fa-upload')
->setDisabled(!$can_publish)
->setWorkflow(true)
->setHref($publish_uri));
}
if ($document->getStatus() == PhrictionDocumentStatus::STATUS_EXISTS) {
$action_view->addAction(
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Move Document'))
->setDisabled(!$can_edit)
@ -309,7 +453,7 @@ final class PhrictionDocumentController
->setHref('/phriction/move/'.$document->getID().'/')
->setWorkflow(true));
$action_view->addAction(
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Delete Document'))
->setDisabled(!$can_edit)
@ -318,23 +462,16 @@ final class PhrictionDocumentController
->setWorkflow(true));
}
$action_view->addAction(
id(new PhabricatorActionView())
->setName(pht('View History'))
->setIcon('fa-list')
->setHref(PhrictionDocument::getSlugURI($slug, 'history')));
$print_uri = PhrictionDocument::getSlugURI($slug).'?__print__=1';
$action_view->addAction(
$curtain->addAction(
id(new PhabricatorActionView())
->setName(pht('Printable Page'))
->setIcon('fa-print')
->setOpenInNewWindow(true)
->setHref($print_uri));
return $action_view;
return $curtain;
}
private function renderDocumentChildren($slug) {

View file

@ -7,7 +7,7 @@ final class PhrictionEditController
$viewer = $request->getViewer();
$id = $request->getURIData('id');
$current_version = null;
$max_version = null;
if ($id) {
$is_new = false;
$document = id(new PhrictionDocumentQuery())
@ -24,7 +24,7 @@ final class PhrictionEditController
return new Aphront404Response();
}
$current_version = $document->getContent()->getVersion();
$max_version = $document->getMaxVersion();
$revert = $request->getInt('revert');
if ($revert) {
@ -37,9 +37,12 @@ final class PhrictionEditController
return new Aphront404Response();
}
} else {
$content = $document->getContent();
$content = id(new PhrictionContentQuery())
->setViewer($viewer)
->withDocumentPHIDs(array($document->getPHID()))
->setLimit(1)
->executeOne();
}
} else {
$slug = $request->getStr('slug');
$slug = PhabricatorSlug::normalize($slug);
@ -54,8 +57,13 @@ final class PhrictionEditController
->executeOne();
if ($document) {
$content = $document->getContent();
$current_version = $content->getVersion();
$content = id(new PhrictionContentQuery())
->setViewer($viewer)
->withDocumentPHIDs(array($document->getPHID()))
->setLimit(1)
->executeOne();
$max_version = $document->getMaxVersion();
$is_new = false;
} else {
$document = PhrictionDocument::initializeNewDocument($viewer, $slug);
@ -128,7 +136,7 @@ final class PhrictionEditController
$title = $request->getStr('title');
$content_text = $request->getStr('content');
$notes = $request->getStr('description');
$current_version = $request->getInt('contentVersion');
$max_version = $request->getInt('contentVersion');
$v_view = $request->getStr('viewPolicy');
$v_edit = $request->getStr('editPolicy');
$v_cc = $request->getArr('cc');
@ -168,7 +176,7 @@ final class PhrictionEditController
->setContinueOnNoEffect(true)
->setDescription($notes)
->setProcessContentVersionError(!$request->getBool('overwrite'))
->setContentVersion($current_version);
->setContentVersion($max_version);
try {
$editor->applyTransactions($document, $xactions);
@ -226,19 +234,13 @@ final class PhrictionEditController
->execute();
$view_capability = PhabricatorPolicyCapability::CAN_VIEW;
$edit_capability = PhabricatorPolicyCapability::CAN_EDIT;
$codex = id(PhabricatorPolicyCodex::newFromObject($document, $viewer))
->setCapability($view_capability);
$view_capability_description = $codex->getPolicySpecialRuleForCapability(
PhabricatorPolicyCapability::CAN_VIEW)->getDescription();
$edit_capability_description = $codex->getPolicySpecialRuleForCapability(
PhabricatorPolicyCapability::CAN_EDIT)->getDescription();
$form = id(new AphrontFormView())
->setUser($viewer)
->addHiddenInput('slug', $document->getSlug())
->addHiddenInput('nodraft', $request->getBool('nodraft'))
->addHiddenInput('contentVersion', $current_version)
->addHiddenInput('contentVersion', $max_version)
->addHiddenInput('overwrite', $overwrite)
->appendChild(
id(new AphrontFormTextControl())
@ -279,15 +281,13 @@ final class PhrictionEditController
->setSpacePHID($v_space)
->setPolicyObject($document)
->setCapability($view_capability)
->setPolicies($policies)
->setCaption($view_capability_description))
->setPolicies($policies))
->appendChild(
id(new AphrontFormPolicyControl())
->setName('editPolicy')
->setPolicyObject($document)
->setCapability($edit_capability)
->setPolicies($policies)
->setCaption($edit_capability_description))
->setPolicies($policies))
->appendChild(
id(new AphrontFormTextControl())
->setLabel(pht('Edit Notes'))
@ -323,17 +323,17 @@ final class PhrictionEditController
$crumbs->setBorder(true);
$view = id(new PHUITwoColumnView())
->setFooter(array(
$draft_note,
$form_box,
$preview,
));
->setFooter(
array(
$draft_note,
$form_box,
$preview,
));
return $this->newPage()
->setTitle($page_title)
->setCrumbs($crumbs)
->appendChild($view);
}
}

View file

@ -31,32 +31,18 @@ final class PhrictionHistoryController
->executeWithCursorPager($pager);
$author_phids = mpull($history, 'getAuthorPHID');
$handles = $this->loadViewerHandles($author_phids);
$handles = $viewer->loadHandles($author_phids);
$max_version = (int)$document->getMaxVersion();
$current_version = $document->getContent()->getVersion();
$list = new PHUIObjectItemListView();
$list->setFlush(true);
foreach ($history as $content) {
$author = $handles[$content->getAuthorPHID()]->renderLink();
$slug_uri = PhrictionDocument::getSlugURI($document->getSlug());
$version = $content->getVersion();
$diff_uri = new PhutilURI('/phriction/diff/'.$document->getID().'/');
$vs_previous = null;
if ($content->getVersion() != 1) {
$vs_previous = $diff_uri
->alter('l', $content->getVersion() - 1)
->alter('r', $content->getVersion());
}
$vs_head = null;
if ($content->getID() != $document->getContentID()) {
$vs_head = $diff_uri
->alter('l', $content->getVersion())
->alter('r', $current->getVersion());
}
$base_uri = new PhutilURI('/phriction/diff/'.$document->getID().'/');
$change_type = PhrictionChangeType::getChangeTypeLabel(
$content->getChangeType());
@ -68,63 +54,90 @@ final class PhrictionHistoryController
$color = 'lightbluetext';
break;
case PhrictionChangeType::CHANGE_MOVE_HERE:
$color = 'yellow';
$color = 'yellow';
break;
case PhrictionChangeType::CHANGE_MOVE_AWAY:
$color = 'orange';
$color = 'orange';
break;
case PhrictionChangeType::CHANGE_STUB:
$color = 'green';
break;
default:
throw new Exception(pht('Unknown change type!'));
$color = 'indigo';
break;
}
$version_uri = $slug_uri.'?v='.$version;
$item = id(new PHUIObjectItemView())
->setHeader(pht('%s by %s', $change_type, $author))
->setStatusIcon('fa-file '.$color)
->addAttribute(
phutil_tag(
'a',
array(
'href' => $slug_uri.'?v='.$version,
),
pht('Version %s', $version)))
->addAttribute(pht('%s %s',
phabricator_date($content->getDateCreated(), $viewer),
phabricator_time($content->getDateCreated(), $viewer)));
->setHref($version_uri);
if ($content->getDescription()) {
$item->addAttribute($content->getDescription());
}
if ($vs_previous) {
$item->addIcon(
'fa-reply',
pht('Show Change'),
array(
'href' => $vs_previous,
));
if ($version > $current_version) {
$icon = 'fa-spinner';
$color = 'pink';
$header = pht('Draft %d', $version);
} else {
$item->addIcon(
'fa-reply grey',
phutil_tag('em', array(), pht('No previous change')));
$icon = 'fa-file-o';
$header = pht('Version %d', $version);
}
if ($vs_head) {
$item->addIcon(
'fa-reply-all',
pht('Show Later Changes'),
array(
'href' => $vs_head,
));
} else {
$item->addIcon(
'fa-reply-all grey',
phutil_tag('em', array(), pht('No later changes')));
if ($version == $current_version) {
$item->setEffect('selected');
}
$item
->setHeader($header)
->setStatusIcon($icon.' '.$color);
$description = $content->getDescription();
if (strlen($description)) {
$item->addAttribute($description);
}
$item->addIcon(
null,
phabricator_datetime($content->getDateCreated(), $viewer));
$author_phid = $content->getAuthorPHID();
$item->addByline($viewer->renderHandle($author_phid));
$diff_uri = null;
if ($version > 1) {
$diff_uri = $base_uri
->alter('l', $version - 1)
->alter('r', $version);
} else {
$diff_uri = null;
}
if ($content->getVersion() != $max_version) {
$compare_uri = $base_uri
->alter('l', $version)
->alter('r', $max_version);
} else {
$compare_uri = null;
}
$button_bar = id(new PHUIButtonBarView())
->addButton(
id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-chevron-down')
->setDisabled(!$diff_uri)
->setHref($diff_uri)
->setText(pht('Diff')))
->addButton(
id(new PHUIButtonView())
->setTag('a')
->setColor('grey')
->setIcon('fa-chevron-circle-up')
->setDisabled(!$compare_uri)
->setHref($compare_uri)
->setText(pht('Compare')));
$item->setSideColumn($button_bar);
$list->addItem($item);
}

View file

@ -0,0 +1,86 @@
<?php
final class PhrictionPublishController
extends PhrictionController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$id = $request->getURIData('documentID');
$content_id = $request->getURIData('contentID');
$document = id(new PhrictionDocumentQuery())
->setViewer($viewer)
->withIDs(array($id))
->needContent(true)
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_EDIT,
PhabricatorPolicyCapability::CAN_VIEW,
))
->executeOne();
if (!$document) {
return new Aphront404Response();
}
$document_uri = $document->getURI();
$content = id(new PhrictionContentQuery())
->setViewer($viewer)
->withIDs(array($content_id))
->executeOne();
if (!$content) {
return new Aphront404Response();
}
if ($content->getPHID() == $document->getContentPHID()) {
return $this->newDialog()
->setTitle(pht('Already Published'))
->appendChild(
pht(
'This version of the document is already the published '.
'version.'))
->addCancelButton($document_uri);
}
$content_uri = $document_uri.'?v='.$content->getVersion();
if ($request->isFormPost()) {
$xactions = array();
$xactions[] = id(new PhrictionTransaction())
->setTransactionType(
PhrictionDocumentPublishTransaction::TRANSACTIONTYPE)
->setNewValue($content->getPHID());
id(new PhrictionTransactionEditor())
->setActor($viewer)
->setContentSourceFromRequest($request)
->setContinueOnNoEffect(true)
->setContinueOnMissingFields(true)
->applyTransactions($document, $xactions);
return id(new AphrontRedirectResponse())->setURI($document_uri);
}
if ($content->getVersion() < $document->getContent()->getVersion()) {
$title = pht('Revert Document?');
$body = pht(
'Revert the published version of this document to an older '.
'version?');
$button = pht('Revert');
} else {
$title = pht('Publish Draft?');
$body = pht(
'Update the published version of this document to this newer '.
'version?');
$button = pht('Publish');
}
return $this->newDialog()
->setTitle($title)
->appendChild($body)
->addSubmitButton($button)
->addCancelButton($content_uri);
}
}

View file

@ -93,29 +93,13 @@ final class PhrictionTransactionEditor
return $types;
}
protected function shouldApplyInitialEffects(
PhabricatorLiskDAO $object,
array $xactions) {
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE:
case PhrictionDocumentContentTransaction::TRANSACTIONTYPE:
case PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE:
case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE:
case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE:
return true;
}
}
return parent::shouldApplyInitialEffects($object, $xactions);
}
protected function applyInitialEffects(
protected function expandTransactions(
PhabricatorLiskDAO $object,
array $xactions) {
$this->setOldContent($object->getContent());
$this->setNewContent($this->buildNewContentTemplate($object));
return parent::expandTransactions($object, $xactions);
}
protected function expandTransaction(
@ -148,7 +132,6 @@ final class PhrictionTransactionEditor
break;
default:
break;
}
return $xactions;
@ -158,29 +141,12 @@ final class PhrictionTransactionEditor
PhabricatorLiskDAO $object,
array $xactions) {
$save_content = false;
foreach ($xactions as $xaction) {
switch ($xaction->getTransactionType()) {
case PhrictionDocumentTitleTransaction::TRANSACTIONTYPE:
case PhrictionDocumentMoveToTransaction::TRANSACTIONTYPE:
case PhrictionDocumentMoveAwayTransaction::TRANSACTIONTYPE:
case PhrictionDocumentDeleteTransaction::TRANSACTIONTYPE:
case PhrictionDocumentContentTransaction::TRANSACTIONTYPE:
$save_content = true;
break;
default:
break;
}
}
if ($this->hasNewDocumentContent()) {
$content = $this->getNewDocumentContent($object);
if ($save_content) {
$content = $this->getNewContent();
$content->setDocumentID($object->getID());
$content->save();
$object->setContentID($content->getID());
$object->save();
$object->attachContent($content);
$content
->setDocumentPHID($object->getPHID())
->save();
}
if ($this->getIsNewObject() && !$this->getSkipAncestorCheck()) {
@ -502,7 +468,7 @@ final class PhrictionTransactionEditor
$error = null;
if ($this->getContentVersion() &&
($object->getContent()->getVersion() != $this->getContentVersion())) {
($object->getMaxVersion() != $this->getContentVersion())) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Edit Conflict'),
@ -535,24 +501,48 @@ final class PhrictionTransactionEditor
->setDocument($object);
}
private function buildNewContentTemplate(
PhrictionDocument $document) {
private function hasNewDocumentContent() {
return (bool)$this->newContent;
}
$new_content = id(new PhrictionContent())
public function getNewDocumentContent(PhrictionDocument $document) {
if (!$this->hasNewDocumentContent()) {
$content = $this->newDocumentContent($document);
// Generate a PHID now so we can populate "contentPHID" before saving
// the document to the database: the column is not nullable so we need
// a value.
$content_phid = $content->generatePHID();
$content->setPHID($content_phid);
$document->setContentPHID($content_phid);
$document->attachContent($content);
$document->setEditedEpoch(PhabricatorTime::getNow());
$document->setMaxVersion($content->getVersion());
$this->newContent = $content;
}
return $this->newContent;
}
private function newDocumentContent(PhrictionDocument $document) {
$content = id(new PhrictionContent())
->setSlug($document->getSlug())
->setAuthorPHID($this->getActor()->getPHID())
->setAuthorPHID($this->getActingAsPHID())
->setChangeType(PhrictionChangeType::CHANGE_EDIT)
->setTitle($this->getOldContent()->getTitle())
->setContent($this->getOldContent()->getContent())
->setDescription('');
if (strlen($this->getDescription())) {
$new_content->setDescription($this->getDescription());
$content->setDescription($this->getDescription());
}
$new_content->setVersion($this->getOldContent()->getVersion() + 1);
$content->setVersion($document->getMaxVersion() + 1);
return $new_content;
return $content;
}
protected function getCustomWorkerState() {

View file

@ -76,7 +76,7 @@ final class PhrictionContentQuery
if ($this->shouldJoinDocumentTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T d ON d.id = c.documentID',
'JOIN %T d ON d.phid = c.documentPHID',
id(new PhrictionDocument())->getTableName());
}
@ -84,19 +84,19 @@ final class PhrictionContentQuery
}
protected function willFilterPage(array $contents) {
$document_ids = mpull($contents, 'getDocumentID');
$document_phids = mpull($contents, 'getDocumentPHID');
$documents = id(new PhrictionDocumentQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withIDs($document_ids)
->withPHIDs($document_phids)
->execute();
$documents = mpull($documents, null, 'getID');
$documents = mpull($documents, null, 'getPHID');
foreach ($contents as $key => $content) {
$document_id = $content->getDocumentID();
$document_phid = $content->getDocumentPHID();
$document = idx($documents, $document_id);
$document = idx($documents, $document_phid);
if (!$document) {
unset($contents[$key]);
$this->didRejectResult($content);

View file

@ -151,17 +151,17 @@ final class PhrictionDocumentQuery
$contents = id(new PhrictionContentQuery())
->setViewer($this->getViewer())
->setParentQuery($this)
->withIDs(mpull($documents, 'getContentID'))
->withPHIDs(mpull($documents, 'getContentPHID'))
->execute();
$contents = mpull($contents, null, 'getID');
$contents = mpull($contents, null, 'getPHID');
foreach ($documents as $key => $document) {
$content_id = $document->getContentID();
if (empty($contents[$content_id])) {
$content_phid = $document->getContentPHID();
if (empty($contents[$content_phid])) {
unset($documents[$key]);
continue;
}
$document->attachContent($contents[$content_id]);
$document->attachContent($contents[$content_phid]);
}
}
@ -175,7 +175,7 @@ final class PhrictionDocumentQuery
$content_dao = new PhrictionContent();
$joins[] = qsprintf(
$conn,
'JOIN %T c ON d.contentID = c.id',
'JOIN %T c ON d.contentPHID = c.phid',
$content_dao->getTableName());
}
@ -321,7 +321,7 @@ final class PhrictionDocumentQuery
public function getBuiltinOrders() {
return parent::getBuiltinOrders() + array(
self::ORDER_HIERARCHY => array(
'vector' => array('depth', 'title', 'updated'),
'vector' => array('depth', 'title', 'updated', 'id'),
'name' => pht('Hierarchy'),
),
);
@ -343,9 +343,9 @@ final class PhrictionDocumentQuery
),
'updated' => array(
'table' => 'd',
'column' => 'contentID',
'column' => 'editedEpoch',
'type' => 'int',
'unique' => true,
'unique' => false,
),
);
}
@ -356,7 +356,7 @@ final class PhrictionDocumentQuery
$map = array(
'id' => $document->getID(),
'depth' => $document->getDepth(),
'updated' => $document->getContentID(),
'updated' => $document->getEditedEpoch(),
);
foreach ($keys as $key) {

View file

@ -7,7 +7,7 @@ final class PhrictionContent
PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface {
protected $documentID;
protected $documentPHID;
protected $version;
protected $authorPHID;
@ -34,8 +34,8 @@ final class PhrictionContent
'description' => 'text',
),
self::CONFIG_KEY_SCHEMA => array(
'documentID' => array(
'columns' => array('documentID', 'version'),
'key_version' => array(
'columns' => array('documentPHID', 'version'),
'unique' => true,
),
'authorPHID' => array(

View file

@ -17,12 +17,13 @@ final class PhrictionDocument extends PhrictionDAO
protected $slug;
protected $depth;
protected $contentID;
protected $contentPHID;
protected $status;
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
protected $spacePHID;
protected $editedEpoch;
protected $maxVersion;
private $contentObject = self::ATTACHABLE;
private $ancestors = array();
@ -34,16 +35,11 @@ final class PhrictionDocument extends PhrictionDAO
self::CONFIG_COLUMN_SCHEMA => array(
'slug' => 'sort128',
'depth' => 'uint32',
'contentID' => 'id?',
'status' => 'text32',
'mailKey' => 'bytes20',
'editedEpoch' => 'epoch',
'maxVersion' => 'uint32',
),
self::CONFIG_KEY_SCHEMA => array(
'key_phid' => null,
'phid' => array(
'columns' => array('phid'),
'unique' => true,
),
'slug' => array(
'columns' => array('slug'),
'unique' => true,
@ -56,17 +52,16 @@ final class PhrictionDocument extends PhrictionDAO
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhrictionDocumentPHIDType::TYPECONST);
public function getPHIDType() {
return PhrictionDocumentPHIDType::TYPECONST;
}
public static function initializeNewDocument(PhabricatorUser $actor, $slug) {
$document = new PhrictionDocument();
$document->setSlug($slug);
$document = id(new self())
->setSlug($slug);
$content = new PhrictionContent();
$content->setSlug($slug);
$content = id(new PhrictionContent())
->setSlug($slug);
$default_title = PhabricatorSlug::getDefaultTitle($slug);
$content->setTitle($default_title);
@ -95,14 +90,10 @@ final class PhrictionDocument extends PhrictionDAO
->setSpacePHID($actor->getDefaultSpacePHID());
}
return $document;
}
$document->setEditedEpoch(PhabricatorTime::getNow());
$document->setMaxVersion(0);
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
return $document;
}
public static function getSlugURI($slug, $type = 'document') {
@ -332,9 +323,9 @@ final class PhrictionDocument extends PhrictionDAO
/* -( PhabricatorPolicyCodexInterface )------------------------------------ */
public function newPolicyCodex() {
return new PhrictionDocumentPolicyCodex();
}
public function newPolicyCodex() {
return new PhrictionDocumentPolicyCodex();
}
}

View file

@ -1,7 +1,7 @@
<?php
final class PhrictionDocumentContentTransaction
extends PhrictionDocumentTransactionType {
extends PhrictionDocumentVersionTransaction {
const TRANSACTIONTYPE = 'content';
@ -18,10 +18,9 @@ final class PhrictionDocumentContentTransaction
public function applyInternalEffects($object, $value) {
$object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS);
}
public function applyExternalEffects($object, $value) {
$this->getEditor()->getNewContent()->setContent($value);
$content = $this->getNewDocumentContent($object);
$content->setContent($value);
}
public function shouldHide() {

View file

@ -1,7 +1,7 @@
<?php
final class PhrictionDocumentDeleteTransaction
extends PhrictionDocumentTransactionType {
extends PhrictionDocumentVersionTransaction {
const TRANSACTIONTYPE = 'delete';
@ -11,12 +11,11 @@ final class PhrictionDocumentDeleteTransaction
public function applyInternalEffects($object, $value) {
$object->setStatus(PhrictionDocumentStatus::STATUS_DELETED);
}
public function applyExternalEffects($object, $value) {
$this->getEditor()->getNewContent()->setContent('');
$this->getEditor()->getNewContent()->setChangeType(
PhrictionChangeType::CHANGE_DELETE);
$content = $this->getNewDocumentContent($object);
$content->setContent('');
$content->setChangeType(PhrictionChangeType::CHANGE_DELETE);
}
public function getActionStrength() {

View file

@ -1,7 +1,7 @@
<?php
final class PhrictionDocumentMoveAwayTransaction
extends PhrictionDocumentTransactionType {
extends PhrictionDocumentVersionTransaction {
const TRANSACTIONTYPE = 'move-away';
@ -22,14 +22,12 @@ final class PhrictionDocumentMoveAwayTransaction
public function applyInternalEffects($object, $value) {
$object->setStatus(PhrictionDocumentStatus::STATUS_MOVED);
}
public function applyExternalEffects($object, $value) {
$dict = $value;
$this->getEditor()->getNewContent()->setContent('');
$this->getEditor()->getNewContent()->setChangeType(
PhrictionChangeType::CHANGE_MOVE_AWAY);
$this->getEditor()->getNewContent()->setChangeRef($dict['id']);
$content = $this->getNewDocumentContent($object);
$content->setContent('');
$content->setChangeType(PhrictionChangeType::CHANGE_MOVE_AWAY);
$content->setChangeRef($value['id']);
}
public function getActionName() {

View file

@ -1,7 +1,7 @@
<?php
final class PhrictionDocumentMoveToTransaction
extends PhrictionDocumentTransactionType {
extends PhrictionDocumentVersionTransaction {
const TRANSACTIONTYPE = 'move-to';
@ -11,6 +11,7 @@ final class PhrictionDocumentMoveToTransaction
public function generateNewValue($object, $value) {
$document = $value;
$dict = array(
'id' => $document->getID(),
'phid' => $document->getPHID(),
@ -26,15 +27,13 @@ final class PhrictionDocumentMoveToTransaction
public function applyInternalEffects($object, $value) {
$object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS);
}
public function applyExternalEffects($object, $value) {
$dict = $value;
$this->getEditor()->getNewContent()->setContent($dict['content']);
$this->getEditor()->getNewContent()->setTitle($dict['title']);
$this->getEditor()->getNewContent()->setChangeType(
PhrictionChangeType::CHANGE_MOVE_HERE);
$this->getEditor()->getNewContent()->setChangeRef($dict['id']);
$content = $this->getNewDocumentContent($object);
$content->setContent($value['content']);
$content->setTitle($value['title']);
$content->setChangeType(PhrictionChangeType::CHANGE_MOVE_HERE);
$content->setChangeRef($value['id']);
}
public function getActionStrength() {

View file

@ -0,0 +1,71 @@
<?php
final class PhrictionDocumentPublishTransaction
extends PhrictionDocumentTransactionType {
const TRANSACTIONTYPE = 'publish';
public function generateOldValue($object) {
return $object->getContentPHID();
}
public function applyInternalEffects($object, $value) {
$object->setContentPHID($value);
}
public function getActionName() {
return pht('Published');
}
public function getTitle() {
return pht(
'%s published a new version of this document.',
$this->renderAuthor());
}
public function getTitleForFeed() {
return pht(
'%s published a new version of %s.',
$this->renderAuthor(),
$this->renderObject());
}
public function validateTransactions($object, array $xactions) {
$actor = $this->getActor();
$errors = array();
foreach ($xactions as $xaction) {
$content_phid = $xaction->getNewValue();
// If this isn't changing anything, skip it.
if ($content_phid === $object->getContentPHID()) {
continue;
}
$content = id(new PhrictionContentQuery())
->setViewer($actor)
->withPHIDs(array($content_phid))
->executeOne();
if (!$content) {
$errors[] = $this->newInvalidError(
pht(
'Unable to load Content object with PHID "%s".',
$content_phid),
$xaction);
continue;
}
if ($content->getDocumentPHID() !== $object->getPHID()) {
$errors[] = $this->newInvalidError(
pht(
'Content object "%s" can not be published because it belongs '.
'to a different document.',
$content_phid));
continue;
}
}
return $errors;
}
}

View file

@ -1,7 +1,7 @@
<?php
final class PhrictionDocumentTitleTransaction
extends PhrictionDocumentTransactionType {
extends PhrictionDocumentVersionTransaction {
const TRANSACTIONTYPE = 'title';
@ -14,10 +14,10 @@ final class PhrictionDocumentTitleTransaction
public function applyInternalEffects($object, $value) {
$object->setStatus(PhrictionDocumentStatus::STATUS_EXISTS);
}
public function applyExternalEffects($object, $value) {
$this->getEditor()->getNewContent()->setTitle($value);
$content = $this->getNewDocumentContent($object);
$content->setTitle($value);
}
public function getActionStrength() {

View file

@ -0,0 +1,10 @@
<?php
abstract class PhrictionDocumentVersionTransaction
extends PhrictionDocumentTransactionType {
protected function getNewDocumentContent($object) {
return $this->getEditor()->getNewDocumentContent($object);
}
}

View file

@ -115,58 +115,6 @@ final class PhabricatorProjectTransactionEditor
return $errors;
}
protected function requireCapabilities(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_EDGE:
switch ($xaction->getMetadataValue('edge:type')) {
case PhabricatorProjectProjectHasMemberEdgeType::EDGECONST:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$add = array_keys(array_diff_key($new, $old));
$rem = array_keys(array_diff_key($old, $new));
$actor_phid = $this->requireActor()->getPHID();
$is_join = (($add === array($actor_phid)) && !$rem);
$is_leave = (($rem === array($actor_phid)) && !$add);
if ($is_join) {
// You need CAN_JOIN to join a project.
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_JOIN);
} else if ($is_leave) {
// You usually don't need any capabilities to leave a project.
if ($object->getIsMembershipLocked()) {
// you must be able to edit though to leave locked projects
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
}
} else {
if (!$this->getIsNewObject()) {
// You need CAN_EDIT to change members other than yourself.
// (PHI193) Just skip this check if we're creating a project.
PhabricatorPolicyFilter::requireCapability(
$this->requireActor(),
$object,
PhabricatorPolicyCapability::CAN_EDIT);
}
}
return;
}
break;
}
return parent::requireCapabilities($object, $xaction);
}
protected function willPublish(PhabricatorLiskDAO $object, array $xactions) {
// NOTE: We're using the omnipotent user here because the original actor
// may no longer have permission to view the object.

View file

@ -200,6 +200,8 @@ final class PhabricatorRepositoryCommit
$this->authorIdentity = $author;
$this->committerIdentity = $committer;
return $this;
}
public function getAuthorIdentity() {
@ -485,6 +487,23 @@ final class PhabricatorRepositoryCommit
return null;
}
public function loadIdentities(PhabricatorUser $viewer) {
if ($this->authorIdentity !== self::ATTACHABLE) {
return $this;
}
$commit = id(new DiffusionCommitQuery())
->setViewer($viewer)
->withIDs(array($this->getID()))
->needIdentities(true)
->executeOne();
$author_identity = $commit->getAuthorIdentity();
$committer_identity = $commit->getCommitterIdentity();
return $this->attachIdentities($author_identity, $committer_identity);
}
public function hasCommitterIdentity() {
return ($this->getCommitterIdentity() !== null);
}

View file

@ -65,6 +65,16 @@ final class PhabricatorRepositoryIdentity
$this->getIdentityNameEncoding());
}
public function getIdentityEmailAddress() {
$address = new PhutilEmailAddress($this->getIdentityName());
return $address->getAddress();
}
public function getIdentityDisplayName() {
$address = new PhutilEmailAddress($this->getIdentityName());
return $address->getDisplayName();
}
public function getIdentityShortName() {
// TODO
return $this->getIdentityName();

View file

@ -44,8 +44,6 @@ final class PhabricatorSubscriptionsMuteController
);
}
$muted_type = PhabricatorMutedByEdgeType::EDGECONST;
$xaction = id($object->getApplicationTransactionTemplate())
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
->setMetadataValue('edge:type', $muted_type)

View file

@ -1679,7 +1679,7 @@ abstract class PhabricatorEditEngine
->setUser($viewer)
->setFields($fields);
$document = id(new PHUIDocumentViewPro())
$document = id(new PHUIDocumentView())
->setUser($viewer)
->setHeader($header)
->appendChild($help_view);
@ -2003,7 +2003,19 @@ abstract class PhabricatorEditEngine
$identifier = $request->getValue('objectIdentifier');
if ($identifier) {
$this->setIsCreate(false);
$object = $this->newObjectFromIdentifier($identifier);
// After T13186, each transaction can individually weaken or replace the
// capabilities required to apply it, so we no longer need CAN_EDIT to
// attempt to apply transactions to objects. In practice, almost all
// transactions require CAN_EDIT so we won't get very far if we don't
// have it.
$capabilities = array(
PhabricatorPolicyCapability::CAN_VIEW,
);
$object = $this->newObjectFromIdentifier(
$identifier,
$capabilities);
} else {
$this->requireCreateCapability();

View file

@ -976,6 +976,27 @@ abstract class PhabricatorApplicationTransactionEditor
$this->adjustTransactionValues($object, $xaction);
}
// Now that we've merged and combined transactions, check for required
// capabilities. Note that we're doing this before filtering
// transactions: if you try to apply an edit which you do not have
// permission to apply, we want to give you a permissions error even
// if the edit would have no effect.
$this->applyCapabilityChecks($object, $xactions);
// See T13186. Fatal hard if this object has an older
// "requireCapabilities()" method. The code may rely on this method being
// called to apply policy checks, so err on the side of safety and fatal.
// TODO: Remove this check after some time has passed.
if (method_exists($this, 'requireCapabilities')) {
throw new Exception(
pht(
'Editor (of class "%s") implements obsolete policy method '.
'requireCapabilities(). The implementation for this Editor '.
'MUST be updated. See <%s> for discussion.',
get_class($this),
'https://secure.phabricator.com/T13186'));
}
$xactions = $this->filterTransactions($object, $xactions);
// TODO: Once everything is on EditEngine, just use getIsNewObject() to
@ -994,12 +1015,6 @@ abstract class PhabricatorApplicationTransactionEditor
}
}
// Now that we've merged, filtered, and combined transactions, check for
// required capabilities.
foreach ($xactions as $xaction) {
$this->requireCapabilities($object, $xaction);
}
$xactions = $this->sortTransactions($xactions);
$file_phids = $this->extractFilePHIDs($object, $xactions);
@ -1459,34 +1474,151 @@ abstract class PhabricatorApplicationTransactionEditor
}
}
protected function requireCapabilities(
private function applyCapabilityChecks(
PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) {
array $xactions) {
assert_instances_of($xactions, 'PhabricatorApplicationTransaction');
$can_edit = PhabricatorPolicyCapability::CAN_EDIT;
if ($this->getIsNewObject()) {
return;
// If we're creating a new object, we don't need any special capabilities
// on the object. The actor has already made it through creation checks,
// and objects which haven't been created yet often can not be
// meaningfully tested for capabilities anyway.
$required_capabilities = array();
} else {
if (!$xactions && !$this->xactions) {
// If we aren't doing anything, require CAN_EDIT to improve consistency.
$required_capabilities = array($can_edit);
} else {
$required_capabilities = array();
foreach ($xactions as $xaction) {
$type = $xaction->getTransactionType();
$xtype = $this->getModularTransactionType($type);
if (!$xtype) {
$capabilities = $this->getLegacyRequiredCapabilities($xaction);
} else {
$capabilities = $xtype->getRequiredCapabilities($object, $xaction);
}
// For convenience, we allow flexibility in the return types because
// it's very unusual that a transaction actually requires multiple
// capability checks.
if ($capabilities === null) {
$capabilities = array();
} else {
$capabilities = (array)$capabilities;
}
foreach ($capabilities as $capability) {
$required_capabilities[$capability] = $capability;
}
}
}
}
$actor = $this->requireActor();
switch ($xaction->getTransactionType()) {
case PhabricatorTransactions::TYPE_COMMENT:
PhabricatorPolicyFilter::requireCapability(
$actor,
$object,
PhabricatorPolicyCapability::CAN_VIEW);
break;
case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY:
case PhabricatorTransactions::TYPE_JOIN_POLICY:
case PhabricatorTransactions::TYPE_SPACE:
PhabricatorPolicyFilter::requireCapability(
$actor,
$object,
PhabricatorPolicyCapability::CAN_EDIT);
break;
$required_capabilities = array_fuse($required_capabilities);
$actor = $this->getActor();
if ($required_capabilities) {
id(new PhabricatorPolicyFilter())
->setViewer($actor)
->requireCapabilities($required_capabilities)
->raisePolicyExceptions(true)
->apply(array($object));
}
}
private function getLegacyRequiredCapabilities(
PhabricatorApplicationTransaction $xaction) {
$type = $xaction->getTransactionType();
switch ($type) {
case PhabricatorTransactions::TYPE_COMMENT:
// TODO: Comments technically require CAN_INTERACT, but this is
// currently somewhat special and handled through EditEngine. For now,
// don't enforce it here.
return null;
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
// TODO: Removing subscribers other than yourself should probably
// require CAN_EDIT permission. You can do this via the API but
// generally can not via the web interface.
return null;
case PhabricatorTransactions::TYPE_TOKEN:
// TODO: This technically requires CAN_INTERACT, like comments.
return null;
case PhabricatorTransactions::TYPE_HISTORY:
// This is a special magic transaction which sends you history via
// email and is only partially supported in the upstream. You don't
// need any capabilities to apply it.
return null;
case PhabricatorTransactions::TYPE_EDGE:
return $this->getLegacyRequiredEdgeCapabilities($xaction);
default:
// For other older (non-modular) transactions, always require exactly
// CAN_EDIT. Transactions which do not need CAN_EDIT or need additional
// capabilities must move to ModularTransactions.
return PhabricatorPolicyCapability::CAN_EDIT;
}
}
private function getLegacyRequiredEdgeCapabilities(
PhabricatorApplicationTransaction $xaction) {
// You don't need to have edit permission on an object to mention it or
// otherwise add a relationship pointing toward it.
if ($this->getIsInverseEdgeEditor()) {
return null;
}
$edge_type = $xaction->getMetadataValue('edge:type');
switch ($edge_type) {
case PhabricatorMutedByEdgeType::EDGECONST:
// At time of writing, you can only write this edge for yourself, so
// you don't need permissions. If you can eventually mute an object
// for other users, this would need to be revisited.
return null;
case PhabricatorObjectMentionsObjectEdgeType::EDGECONST:
return null;
case PhabricatorProjectProjectHasMemberEdgeType::EDGECONST:
$old = $xaction->getOldValue();
$new = $xaction->getNewValue();
$add = array_keys(array_diff_key($new, $old));
$rem = array_keys(array_diff_key($old, $new));
$actor_phid = $this->requireActor()->getPHID();
$is_join = (($add === array($actor_phid)) && !$rem);
$is_leave = (($rem === array($actor_phid)) && !$add);
if ($is_join) {
// You need CAN_JOIN to join a project.
return PhabricatorPolicyCapability::CAN_JOIN;
}
if ($is_leave) {
$object = $this->object;
// You usually don't need any capabilities to leave a project...
if ($object->getIsMembershipLocked()) {
// ...you must be able to edit to leave locked projects, though.
return PhabricatorPolicyCapability::CAN_EDIT;
} else {
return null;
}
}
// You need CAN_EDIT to change members other than yourself.
return PhabricatorPolicyCapability::CAN_EDIT;
default:
return PhabricatorPolicyCapability::CAN_EDIT;
}
}
private function buildSubscribeTransaction(
PhabricatorLiskDAO $object,
array $xactions,

View file

@ -366,4 +366,30 @@ abstract class PhabricatorModularTransactionType
$capability);
}
/**
* Get a list of capabilities the actor must have on the object to apply
* a transaction to it.
*
* Usually, you should use this to reduce capability requirements when a
* transaction (like leaving a Conpherence thread) can be applied without
* having edit permission on the object. You can override this method to
* remove the CAN_EDIT requirement, or to replace it with a different
* requirement.
*
* If you are increasing capability requirements and need to add an
* additional capability or policy requirement above and beyond CAN_EDIT, it
* is usually better implemented as a validation check.
*
* @param object Object being edited.
* @param PhabricatorApplicationTransaction Transaction being applied.
* @return null|const|list<const> A capability constant (or list of
* capability constants) which the actor must have on the object. You can
* return `null` as a shorthand for "no capabilities are required".
*/
public function getRequiredCapabilities(
$object,
PhabricatorApplicationTransaction $xaction) {
return PhabricatorPolicyCapability::CAN_EDIT;
}
}

View file

@ -142,7 +142,7 @@ final class PhabricatorTypeaheadFunctionHelpController
$header = id(new PHUIHeaderView())
->setHeader($title);
$document = id(new PHUIDocumentViewPro())
$document = id(new PHUIDocumentView())
->setHeader($header)
->appendChild($content_box);

View file

@ -279,7 +279,6 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
->setIcon($icon);
}
if ($this->templatePHIDType) {
$context_path = 'template/'.$this->templatePHIDType.'/';
} else {
@ -346,15 +345,6 @@ final class AphrontFormPolicyControl extends AphrontFormControl {
)),
$input,
));
return AphrontFormSelectControl::renderSelectTag(
$this->getValue(),
$this->getOptions(),
array(
'name' => $this->getName(),
'disabled' => $this->getDisabled() ? 'disabled' : null,
'id' => $this->getID(),
));
}
public static function getSelectCustomKey() {

View file

@ -1,6 +1,6 @@
<?php
final class PHUIDocumentViewPro extends AphrontTagView {
final class PHUIDocumentView extends AphrontTagView {
private $header;
private $bookname;
@ -8,6 +8,8 @@ final class PHUIDocumentViewPro extends AphrontTagView {
private $fluid;
private $toc;
private $foot;
private $curtain;
private $banner;
public function setHeader(PHUIHeaderView $header) {
$header->setTall(true);
@ -36,6 +38,24 @@ final class PHUIDocumentViewPro extends AphrontTagView {
return $this;
}
public function setCurtain(PHUICurtainView $curtain) {
$this->curtain = $curtain;
return $this;
}
public function getCurtain() {
return $this->curtain;
}
public function setBanner($banner) {
$this->banner = $banner;
return $this;
}
public function getBanner() {
return $this->banner;
}
protected function getTagAttributes() {
$classes = array();
@ -61,6 +81,17 @@ final class PHUIDocumentViewPro extends AphrontTagView {
$classes[] = 'phui-document-view';
$classes[] = 'phui-document-view-pro';
if ($this->curtain) {
$classes[] = 'has-curtain';
} else {
$classes[] = 'has-no-curtain';
}
if ($this->curtain) {
$action_list = $this->curtain->getActionList();
$this->header->setActionListID($action_list->getID());
}
$book = null;
if ($this->bookname) {
$book = pht('%s (%s)', $this->bookname, $this->bookdescription);
@ -114,32 +145,52 @@ final class PHUIDocumentViewPro extends AphrontTagView {
$this->foot);
}
$content_inner = phutil_tag(
$curtain = null;
if ($this->curtain) {
$curtain = phutil_tag(
'div',
array(
'class' => 'phui-document-inner',
'class' => 'phui-document-curtain',
),
$this->curtain);
}
$main_content = phutil_tag(
'div',
array(
'class' => 'phui-document-content-view',
),
$main_content);
$content_inner = phutil_tag(
'div',
array(
'class' => 'phui-document-inner',
),
array(
$table_of_contents,
$this->header,
$this->banner,
array(
$table_of_contents,
$this->header,
$curtain,
$main_content,
$foot_content,
));
),
$foot_content,
));
$content = phutil_tag(
'div',
array(
'class' => 'phui-document-content',
),
$content_inner);
return phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
$content);
'div',
array(
'class' => 'phui-document-content',
),
$content_inner);
return phutil_tag(
'div',
array(
'class' => implode(' ', $classes),
),
$content);
}
}

View file

@ -709,8 +709,9 @@ final class PHUIObjectItemView extends AphrontTagView {
}
/* Fixed width, right column container. */
$column3 = null;
if ($this->sideColumn) {
$column2 = phutil_tag(
$column3 = phutil_tag(
'div',
array(
'class' => 'phui-oi-col2 phui-oi-side-column',
@ -731,6 +732,7 @@ final class PHUIObjectItemView extends AphrontTagView {
$column0,
$column1,
$column2,
$column3,
)));
$box = phutil_tag(

View file

@ -105,7 +105,7 @@ final class PHUIRemarkupPreviewPanel extends AphrontTagView {
$header = id(new PHUIHeaderView())
->setHeader(pht('%s (Preview)', $this->header));
$content = id(new PHUIDocumentViewPro())
$content = id(new PHUIDocumentView())
->setHeader($header)
->appendChild($preview);
}

View file

@ -26,11 +26,14 @@
.harbormaster-unit-details {
margin: 8px 0 4px;
overflow: hidden;
white-space: pre;
text-overflow: ellipsis;
color: {$lightgreytext};
}
.harbormaster-unit-details-text {
white-space: pre-wrap;
text-overflow: ellipsis;
}
.harbormaster-log-view-loading {
padding: 8px;
text-align: center;

View file

@ -8,6 +8,35 @@
margin: 0 auto;
}
.phui-document-view.phui-document-view-pro.has-curtain {
max-width: 1132px;
}
.printable .phui-document-view.phui-document-view-pro.has-curtain {
max-width: none;
}
.device-desktop .phui-document-inner {
overflow: hidden;
}
.device-desktop .has-curtain .phui-document-content-view {
padding-right: 332px;
}
.printable .phui-document-content-view {
padding-right: 0;
}
.device-desktop .phui-document-curtain {
float: right;
width: 300px;
}
.printable .phui-document-curtain {
display: none;
}
.phui-document-container {
background-color: {$page.content};
position: relative;

View file

@ -63,7 +63,8 @@
font-size: 14px;
}
.phui-document-view .phui-header-action-links .phui-mobile-menu {
.phui-document-view.has-no-curtain
.phui-header-action-links .phui-mobile-menu {
display: block;
}
@ -71,10 +72,6 @@
padding: 8px;
}
.device-desktop .phui-document-content .phabricator-action-list-view {
display: none;
}
.phui-document-content .phabricator-remarkup .remarkup-code-block {
clear: both;
margin: 16px 0;
@ -103,3 +100,9 @@
.remarkup-code {
font: 13px/18px "Menlo", "Consolas", "Monaco", monospace;
}
.phui-document-version-navigation {
text-align: center;
padding: 8px;
background-color: {$lightgreybackground};
}

View file

@ -259,7 +259,6 @@ body .phui-header-shell.phui-bleed-header
color: {$lightgreytext};
}
.phui-header-action-links .phui-mobile-menu {
display: none;
}

View file

@ -20,10 +20,13 @@ JX.behavior('phabricator-nav', function(config) {
// - Flexible Navigation Column ------------------------------------------------
var dragging;
var track;
var collapsed = config.collapsed;
var narrowed;
var visible = null;
JX.enableDispatch(document.body, 'mousemove');
JX.DOM.listen(drag, 'mousedown', null, function(e) {
@ -95,6 +98,7 @@ JX.behavior('phabricator-nav', function(config) {
if (!dragging) {
return;
}
JX.DOM.alterClass(document.body, 'jx-drag-col', false);
dragging = false;
@ -117,6 +121,29 @@ JX.behavior('phabricator-nav', function(config) {
return (JX.$V(drag).x - JX.Vector.getScroll().x);
}
function repaint() {
narrowed = !JX.Device.isDesktop();
var was_visible = visible;
visible = (!collapsed && !narrowed);
if (was_visible === visible) {
return;
}
if (!visible) {
savedrag();
}
JX.DOM.alterClass(main, 'has-local-nav', visible);
JX.DOM.alterClass(main, 'has-drag-nav', visible);
JX.DOM.alterClass(main, 'has-closed-nav', !visible);
if (visible) {
restoredrag();
}
}
var saved_width = config.width;
function savedrag() {
saved_width = get_width();
@ -136,21 +163,10 @@ JX.behavior('phabricator-nav', function(config) {
content.style.marginLeft = (saved_width + JX.Vector.getDim(drag).x) + 'px';
}
var collapsed = config.collapsed;
JX.Stratcom.listen('differential-filetree-toggle', null, function() {
collapsed = !collapsed;
if (collapsed) {
savedrag();
}
JX.DOM.alterClass(main, 'has-local-nav', !collapsed);
JX.DOM.alterClass(main, 'has-drag-nav', !collapsed);
JX.DOM.alterClass(main, 'has-closed-nav', collapsed);
if (!collapsed) {
restoredrag();
}
repaint();
new JX.Request('/settings/adjust/', JX.bag)
.setData({ key : 'nav-collapsed', value : (collapsed ? 1 : 0) })
@ -168,7 +184,9 @@ JX.behavior('phabricator-nav', function(config) {
// of the navigation bar.
function onresize() {
if (JX.Device.getDevice() != 'desktop') {
repaint();
if (!visible) {
return;
}
@ -193,14 +211,13 @@ JX.behavior('phabricator-nav', function(config) {
local.style.left = 0;
JX.Stratcom.listen(['scroll', 'resize'], null, onresize);
onresize();
repaint();
// - Navigation Reset ----------------------------------------------------------
JX.Stratcom.listen('phabricator-device-change', null, function() {
resetdrag();
onresize();
repaint();
});
});