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:
commit
b0130f6d4e
93 changed files with 1544 additions and 681 deletions
|
@ -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',
|
||||
),
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_drydock.drydock_lease
|
||||
ADD acquiredEpoch INT UNSIGNED;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_drydock.drydock_lease
|
||||
ADD activatedEpoch INT UNSIGNED;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
|
||||
ADD contentPHID VARBINARY(64) NOT NULL;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_content
|
||||
ADD documentPHID VARBINARY(64) NOT NULL;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
|
||||
ADD editedEpoch INT UNSIGNED NOT NULL;
|
57
resources/sql/autopatches/20180828.phriction.04.migrate.php
Normal file
57
resources/sql/autopatches/20180828.phriction.04.migrate.php
Normal 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']);
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
|
||||
DROP contentID;
|
|
@ -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.
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_content
|
||||
DROP documentID;
|
|
@ -0,0 +1 @@
|
|||
DELETE FROM {$NAMESPACE}_phriction.phriction_content WHERE documentPHID = '';
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_content
|
||||
ADD UNIQUE KEY `key_version` (documentPHID, version);
|
26
resources/sql/autopatches/20180829.phriction.01.mailkey.php
Normal file
26
resources/sql/autopatches/20180829.phriction.01.mailkey.php
Normal 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());
|
||||
}
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
|
||||
DROP mailKey;
|
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_phriction.phriction_document
|
||||
ADD maxVersion INT UNSIGNED NOT NULL;
|
30
resources/sql/autopatches/20180830.phriction.02.maxes.php
Normal file
30
resources/sql/autopatches/20180830.phriction.02.maxes.php
Normal 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']);
|
||||
}
|
|
@ -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',
|
||||
|
|
|
@ -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(
|
||||
$description = $option->newDescriptionRemarkupView($viewer);
|
||||
if ($description) {
|
||||
$form->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Description'))
|
||||
->setValue($description_view));
|
||||
->setValue($description));
|
||||
}
|
||||
|
||||
if ($group) {
|
||||
|
|
|
@ -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())
|
||||
|
|
|
@ -33,18 +33,16 @@ 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);
|
||||
}
|
||||
|
||||
$title = pht('Setup Issues');
|
||||
$header = $this->buildHeaderView($title);
|
||||
|
||||
} else {
|
||||
$issue_list = array(
|
||||
$important,
|
||||
$php,
|
||||
|
@ -53,6 +51,7 @@ final class PhabricatorConfigIssueListController
|
|||
);
|
||||
|
||||
$issue_list = $this->buildConfigBoxView(pht('Issues'), $issue_list);
|
||||
}
|
||||
|
||||
$crumbs = $this->buildApplicationCrumbs()
|
||||
->addTextCrumb($title)
|
||||
|
@ -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)
|
||||
|
|
|
@ -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 newDescriptionRemarkupView(PhabricatorUser $viewer) {
|
||||
$description = $this->getDescription();
|
||||
if (!strlen($description)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1315,7 +1315,7 @@ final class DifferentialRevisionViewController
|
|||
}
|
||||
|
||||
return id(new HarbormasterUnitSummaryView())
|
||||
->setUser($viewer)
|
||||
->setViewer($viewer)
|
||||
->setExcuse($excuse)
|
||||
->setBuildable($diff->getBuildable())
|
||||
->setUnitMessages($diff->getUnitMessages())
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -34,4 +34,10 @@ final class DiffusionBuildableEngine
|
|||
$this->applyTransactions(array($xaction));
|
||||
}
|
||||
|
||||
public function getAuthorIdentity() {
|
||||
return $this->getObject()
|
||||
->loadIdentities($this->getViewer())
|
||||
->getAuthorIdentity();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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');
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -39,6 +39,7 @@ final class HarbormasterUnitMessageListController
|
|||
}
|
||||
|
||||
$unit = id(new HarbormasterUnitSummaryView())
|
||||
->setViewer($viewer)
|
||||
->setBuildable($buildable)
|
||||
->setUnitMessages($unit_data);
|
||||
|
||||
|
|
|
@ -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'),
|
||||
|
|
|
@ -101,5 +101,8 @@ abstract class HarbormasterBuildableEngine
|
|||
$xactions);
|
||||
}
|
||||
|
||||
public function getAuthorIdentity() {
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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');
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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),
|
||||
$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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
|
|
|
@ -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.');
|
||||
}
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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')
|
||||
|
|
|
@ -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
|
||||
*/
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -85,7 +85,7 @@ final class PhameBlogViewController extends PhameLiveController {
|
|||
}
|
||||
}
|
||||
|
||||
$page = id(new PHUIDocumentViewPro())
|
||||
$page = id(new PHUIDocumentView())
|
||||
->setHeader($header)
|
||||
->appendChild($post_list);
|
||||
|
||||
|
|
|
@ -30,7 +30,7 @@ final class PhamePostViewController
|
|||
$header->setActionList($actions);
|
||||
}
|
||||
|
||||
$document = id(new PHUIDocumentViewPro())
|
||||
$document = id(new PHUIDocumentView())
|
||||
->setHeader($header);
|
||||
|
||||
if ($moved) {
|
||||
|
|
|
@ -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',
|
||||
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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(
|
||||
// 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)));
|
||||
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,21 +334,26 @@ 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(
|
||||
->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) {
|
||||
|
|
|
@ -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,7 +323,8 @@ final class PhrictionEditController
|
|||
$crumbs->setBorder(true);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setFooter(array(
|
||||
->setFooter(
|
||||
array(
|
||||
$draft_note,
|
||||
$form_box,
|
||||
$preview,
|
||||
|
@ -333,7 +334,6 @@ final class PhrictionEditController
|
|||
->setTitle($page_title)
|
||||
->setCrumbs($crumbs)
|
||||
->appendChild($view);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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());
|
||||
|
@ -77,54 +63,81 @@ final class PhrictionHistoryController
|
|||
$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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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') {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
|
@ -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() {
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
abstract class PhrictionDocumentVersionTransaction
|
||||
extends PhrictionDocumentTransactionType {
|
||||
|
||||
protected function getNewDocumentContent($object) {
|
||||
return $this->getEditor()->getNewDocumentContent($object);
|
||||
}
|
||||
|
||||
}
|
|
@ -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.
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -44,8 +44,6 @@ final class PhabricatorSubscriptionsMuteController
|
|||
);
|
||||
}
|
||||
|
||||
$muted_type = PhabricatorMutedByEdgeType::EDGECONST;
|
||||
|
||||
$xaction = id($object->getApplicationTransactionTemplate())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_EDGE)
|
||||
->setMetadataValue('edge:type', $muted_type)
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
$actor = $this->requireActor();
|
||||
switch ($xaction->getTransactionType()) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$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:
|
||||
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;
|
||||
// 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,
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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,6 +145,23 @@ final class PHUIDocumentViewPro extends AphrontTagView {
|
|||
$this->foot);
|
||||
}
|
||||
|
||||
$curtain = null;
|
||||
if ($this->curtain) {
|
||||
$curtain = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'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(
|
||||
|
@ -122,7 +170,11 @@ final class PHUIDocumentViewPro extends AphrontTagView {
|
|||
array(
|
||||
$table_of_contents,
|
||||
$this->header,
|
||||
$this->banner,
|
||||
array(
|
||||
$curtain,
|
||||
$main_content,
|
||||
),
|
||||
$foot_content,
|
||||
));
|
||||
|
||||
|
@ -139,7 +191,6 @@ final class PHUIDocumentViewPro extends AphrontTagView {
|
|||
'class' => implode(' ', $classes),
|
||||
),
|
||||
$content);
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -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(
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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};
|
||||
}
|
||||
|
|
|
@ -259,7 +259,6 @@ body .phui-header-shell.phui-bleed-header
|
|||
color: {$lightgreytext};
|
||||
}
|
||||
|
||||
|
||||
.phui-header-action-links .phui-mobile-menu {
|
||||
display: none;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
});
|
||||
|
||||
});
|
||||
|
|
Loading…
Reference in a new issue