diff --git a/.arclint b/.arclint index 0077bdf518..1b5b63976a 100644 --- a/.arclint +++ b/.arclint @@ -57,13 +57,6 @@ "type": "phutil-library", "include": "(\\.php$)" }, - "phutil-xhpast": { - "type": "phutil-xhpast", - "include": "(\\.php$)", - "phutil-xhpast.deprecated.functions": { - "phutil_escape_html": "The phutil_escape_html() function is deprecated. Raw strings passed to phutil_tag() or hsprintf() are escaped automatically." - } - }, "spelling": { "type": "spelling" }, @@ -73,15 +66,7 @@ "xhpast": { "type": "xhpast", "include": "(\\.php$)", - "severity": { - "16": "advice", - "34": "error" - }, - "xhpast.blacklisted.function": { - "eval": "The eval() function should be avoided. It is potentially unsafe and makes debugging more difficult." - }, - "xhpast.php-version": "5.2.3", - "xhpast.php-version.windows": "5.3.0" + "standard": "phutil.xhpast" } } } diff --git a/resources/celerity/map.php b/resources/celerity/map.php index 8f8209b309..c67682e8f7 100644 --- a/resources/celerity/map.php +++ b/resources/celerity/map.php @@ -7,7 +7,7 @@ */ return array( 'names' => array( - 'core.pkg.css' => 'e94665e4', + 'core.pkg.css' => 'e4f1ea81', 'core.pkg.js' => '47dc9ebb', 'darkconsole.pkg.js' => 'e7393ebb', 'differential.pkg.css' => '2de124c9', @@ -125,8 +125,8 @@ return array( 'rsrc/css/phui/phui-badge.css' => 'f25c3476', 'rsrc/css/phui/phui-box.css' => 'a5bb366d', 'rsrc/css/phui/phui-button.css' => '16020a60', - 'rsrc/css/phui/phui-crumbs-view.css' => 'd842f867', - 'rsrc/css/phui/phui-document-pro.css' => '4f2b42e3', + 'rsrc/css/phui/phui-crumbs-view.css' => '414406b5', + 'rsrc/css/phui/phui-document-pro.css' => '7f3009ce', 'rsrc/css/phui/phui-document.css' => 'f841ad0a', 'rsrc/css/phui/phui-feed-story.css' => 'b7b26d23', 'rsrc/css/phui/phui-fontkit.css' => 'c9d63950', @@ -779,9 +779,9 @@ return array( 'phui-calendar-day-css' => 'd1cf6f93', 'phui-calendar-list-css' => 'c1c7f338', 'phui-calendar-month-css' => '476be7e0', - 'phui-crumbs-view-css' => 'd842f867', + 'phui-crumbs-view-css' => '414406b5', 'phui-document-view-css' => 'f841ad0a', - 'phui-document-view-pro-css' => '4f2b42e3', + 'phui-document-view-pro-css' => '7f3009ce', 'phui-feed-story-css' => 'b7b26d23', 'phui-font-icon-base-css' => 'ecbbb4c2', 'phui-fontkit-css' => 'c9d63950', diff --git a/resources/sql/autopatches/20151106.editengine.1.table.sql b/resources/sql/autopatches/20151106.editengine.1.table.sql new file mode 100644 index 0000000000..bda84d9443 --- /dev/null +++ b/resources/sql/autopatches/20151106.editengine.1.table.sql @@ -0,0 +1,17 @@ +CREATE TABLE {$NAMESPACE}_search.search_editengineconfiguration ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + engineKey VARCHAR(64) NOT NULL COLLATE {$COLLATE_TEXT}, + builtinKey VARCHAR(64) COLLATE {$COLLATE_TEXT}, + name VARCHAR(255) NOT NULL COLLATE {$COLLATE_TEXT}, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + properties LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + isDisabled BOOL NOT NULL DEFAULT 0, + isDefault BOOL NOT NULL DEFAULT 0, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (phid), + UNIQUE KEY `key_engine` (engineKey, builtinKey), + KEY `key_default` (engineKey, isDefault, isDisabled) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151106.editengine.2.xactions.sql b/resources/sql/autopatches/20151106.editengine.2.xactions.sql new file mode 100644 index 0000000000..36a9d7a769 --- /dev/null +++ b/resources/sql/autopatches/20151106.editengine.2.xactions.sql @@ -0,0 +1,19 @@ +CREATE TABLE {$NAMESPACE}_search.search_editengineconfigurationtransaction ( + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, + phid VARBINARY(64) NOT NULL, + authorPHID VARBINARY(64) NOT NULL, + objectPHID VARBINARY(64) NOT NULL, + viewPolicy VARBINARY(64) NOT NULL, + editPolicy VARBINARY(64) NOT NULL, + commentPHID VARBINARY(64) DEFAULT NULL, + commentVersion INT UNSIGNED NOT NULL, + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + UNIQUE KEY `key_phid` (`phid`), + KEY `key_object` (`objectPHID`) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql b/resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql new file mode 100644 index 0000000000..764f50766c --- /dev/null +++ b/resources/sql/autopatches/20151107.phame.blog.mailkey.1.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_blog + ADD mailKey binary(20) NOT NULL; diff --git a/resources/sql/autopatches/20151107.phame.blog.mailkey.2.php b/resources/sql/autopatches/20151107.phame.blog.mailkey.2.php new file mode 100644 index 0000000000..c6f316aa47 --- /dev/null +++ b/resources/sql/autopatches/20151107.phame.blog.mailkey.2.php @@ -0,0 +1,18 @@ +establishConnection('w'); +$iterator = new LiskMigrationIterator($table); +foreach ($iterator as $blog) { + $id = $blog->getID(); + + echo pht('Adding mail key for Blog %d...', $id); + echo "\n"; + + queryfx( + $conn_w, + 'UPDATE %T SET mailKey = %s WHERE id = %d', + $table->getTableName(), + Filesystem::readRandomCharacters(20), + $id); +} diff --git a/resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql b/resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql new file mode 100644 index 0000000000..54aea48bbf --- /dev/null +++ b/resources/sql/autopatches/20151108.phame.blog.joinpolicy.sql @@ -0,0 +1,2 @@ +ALTER TABLE {$NAMESPACE}_phame.phame_blog + DROP joinPolicy; diff --git a/resources/sql/autopatches/20151108.xhpast.stderr.sql b/resources/sql/autopatches/20151108.xhpast.stderr.sql new file mode 100644 index 0000000000..1721505658 --- /dev/null +++ b/resources/sql/autopatches/20151108.xhpast.stderr.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_xhpastview.xhpastview_parsetree + ADD returnCode INT NOT NULL AFTER input; + +ALTER TABLE {$NAMESPACE}_xhpastview.xhpastview_parsetree + ADD stderr longtext NOT NULL AFTER stdout; diff --git a/resources/sql/autopatches/20151109.phame.post.comments.1.sql b/resources/sql/autopatches/20151109.phame.post.comments.1.sql new file mode 100644 index 0000000000..8a36a0d3a7 --- /dev/null +++ b/resources/sql/autopatches/20151109.phame.post.comments.1.sql @@ -0,0 +1,18 @@ +CREATE TABLE {$NAMESPACE}_phame.phame_posttransaction_comment ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + phid VARCHAR(64) NOT NULL, + transactionPHID VARCHAR(64), + authorPHID VARCHAR(64) NOT NULL, + viewPolicy VARCHAR(64) NOT NULL, + editPolicy VARCHAR(64) NOT NULL, + commentVersion INT UNSIGNED NOT NULL, + content LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + contentSource LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}, + isDeleted BOOL NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + dateModified INT UNSIGNED NOT NULL, + + UNIQUE KEY `key_phid` (phid), + UNIQUE KEY `key_version` (transactionPHID, commentVersion) + +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151109.repository.coverage.1.sql b/resources/sql/autopatches/20151109.repository.coverage.1.sql new file mode 100644 index 0000000000..cebea0177c --- /dev/null +++ b/resources/sql/autopatches/20151109.repository.coverage.1.sql @@ -0,0 +1,8 @@ +USE {$NAMESPACE}_repository; +DELETE x FROM repository_coverage x +LEFT JOIN repository_coverage y + ON x.branchID = y.branchID + AND x.commitID = y.commitID + AND x.pathID = y.pathID + AND y.id > x.id + WHERE y.id IS NOT NULL; diff --git a/resources/sql/autopatches/20151109.xhpast.db.1.sql b/resources/sql/autopatches/20151109.xhpast.db.1.sql new file mode 100644 index 0000000000..acf8b36297 --- /dev/null +++ b/resources/sql/autopatches/20151109.xhpast.db.1.sql @@ -0,0 +1,2 @@ +RENAME TABLE {$NAMESPACE}_xhpastview.xhpastview_parsetree + TO {$NAMESPACE}_xhpast.xhpast_parsetree; diff --git a/resources/sql/autopatches/20151109.xhpast.db.2.sql b/resources/sql/autopatches/20151109.xhpast.db.2.sql new file mode 100644 index 0000000000..a4a79bdced --- /dev/null +++ b/resources/sql/autopatches/20151109.xhpast.db.2.sql @@ -0,0 +1 @@ +DROP DATABASE {$NAMESPACE}_xhpastview; diff --git a/resources/sql/autopatches/20151110.daemonenvhash.sql b/resources/sql/autopatches/20151110.daemonenvhash.sql new file mode 100644 index 0000000000..bde4b21741 --- /dev/null +++ b/resources/sql/autopatches/20151110.daemonenvhash.sql @@ -0,0 +1,5 @@ +ALTER TABLE {$NAMESPACE}_daemon.daemon_log + DROP COLUMN envHash; + +ALTER TABLE {$NAMESPACE}_daemon.daemon_log + DROP COLUMN envInfo; diff --git a/resources/sql/autopatches/20151112.herald.edge.sql b/resources/sql/autopatches/20151112.herald.edge.sql new file mode 100644 index 0000000000..db71dfbd79 --- /dev/null +++ b/resources/sql/autopatches/20151112.herald.edge.sql @@ -0,0 +1,16 @@ +CREATE TABLE {$NAMESPACE}_herald.edge ( + src VARBINARY(64) NOT NULL, + type INT UNSIGNED NOT NULL, + dst VARBINARY(64) NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + seq INT UNSIGNED NOT NULL, + dataID INT UNSIGNED, + PRIMARY KEY (src, type, dst), + KEY `src` (src, type, dateCreated, seq), + UNIQUE KEY `key_dst` (dst, type, src) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; + +CREATE TABLE {$NAMESPACE}_herald.edgedata ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT} +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/resources/sql/autopatches/20151116.owners.edge.sql b/resources/sql/autopatches/20151116.owners.edge.sql new file mode 100644 index 0000000000..6d100eacd1 --- /dev/null +++ b/resources/sql/autopatches/20151116.owners.edge.sql @@ -0,0 +1,16 @@ +CREATE TABLE {$NAMESPACE}_owners.edge ( + src VARBINARY(64) NOT NULL, + type INT UNSIGNED NOT NULL, + dst VARBINARY(64) NOT NULL, + dateCreated INT UNSIGNED NOT NULL, + seq INT UNSIGNED NOT NULL, + dataID INT UNSIGNED, + PRIMARY KEY (src, type, dst), + KEY `src` (src, type, dateCreated, seq), + UNIQUE KEY `key_dst` (dst, type, src) +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; + +CREATE TABLE {$NAMESPACE}_owners.edgedata ( + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, + data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT} +) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT}; diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php index aaacc2cde4..73a0fd7393 100644 --- a/src/__phutil_library_map__.php +++ b/src/__phutil_library_map__.php @@ -1556,7 +1556,7 @@ phutil_register_library_map(array( 'PhabricatorActionListView' => 'view/layout/PhabricatorActionListView.php', 'PhabricatorActionView' => 'view/layout/PhabricatorActionView.php', 'PhabricatorActivitySettingsPanel' => 'applications/settings/panel/PhabricatorActivitySettingsPanel.php', - 'PhabricatorAdministratorsPolicyRule' => 'applications/policy/rule/PhabricatorAdministratorsPolicyRule.php', + 'PhabricatorAdministratorsPolicyRule' => 'applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php', 'PhabricatorAjaxRequestExceptionHandler' => 'aphront/handler/PhabricatorAjaxRequestExceptionHandler.php', 'PhabricatorAlmanacApplication' => 'applications/almanac/application/PhabricatorAlmanacApplication.php', 'PhabricatorAmazonAuthProvider' => 'applications/auth/provider/PhabricatorAmazonAuthProvider.php', @@ -1579,8 +1579,6 @@ phutil_register_library_map(array( 'PhabricatorApplicationDatasource' => 'applications/meta/typeahead/PhabricatorApplicationDatasource.php', 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', 'PhabricatorApplicationEditController' => 'applications/meta/controller/PhabricatorApplicationEditController.php', - 'PhabricatorApplicationEditEngine' => 'applications/transactions/editengine/PhabricatorApplicationEditEngine.php', - 'PhabricatorApplicationEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'applications/transactions/view/PhabricatorApplicationEditHTTPParameterHelpView.php', 'PhabricatorApplicationEmailCommandsController' => 'applications/meta/controller/PhabricatorApplicationEmailCommandsController.php', 'PhabricatorApplicationLaunchView' => 'applications/meta/view/PhabricatorApplicationLaunchView.php', @@ -2016,6 +2014,7 @@ phutil_register_library_map(array( 'PhabricatorDaemonManagementStatusWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStatusWorkflow.php', 'PhabricatorDaemonManagementStopWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementStopWorkflow.php', 'PhabricatorDaemonManagementWorkflow' => 'applications/daemon/management/PhabricatorDaemonManagementWorkflow.php', + 'PhabricatorDaemonOverseerModule' => 'infrastructure/daemon/overseer/PhabricatorDaemonOverseerModule.php', 'PhabricatorDaemonReference' => 'infrastructure/daemon/control/PhabricatorDaemonReference.php', 'PhabricatorDaemonTaskGarbageCollector' => 'applications/daemon/garbagecollector/PhabricatorDaemonTaskGarbageCollector.php', 'PhabricatorDaemonTasksTableView' => 'applications/daemon/view/PhabricatorDaemonTasksTableView.php', @@ -2095,7 +2094,6 @@ phutil_register_library_map(array( 'PhabricatorDisabledUserController' => 'applications/auth/controller/PhabricatorDisabledUserController.php', 'PhabricatorDisplayPreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorDisplayPreferencesSettingsPanel.php', 'PhabricatorDisqusAuthProvider' => 'applications/auth/provider/PhabricatorDisqusAuthProvider.php', - 'PhabricatorDisqusConfigOptions' => 'applications/config/option/PhabricatorDisqusConfigOptions.php', 'PhabricatorDivinerApplication' => 'applications/diviner/application/PhabricatorDivinerApplication.php', 'PhabricatorDoorkeeperApplication' => 'applications/doorkeeper/application/PhabricatorDoorkeeperApplication.php', 'PhabricatorDraft' => 'applications/draft/storage/PhabricatorDraft.php', @@ -2111,6 +2109,24 @@ phutil_register_library_map(array( 'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php', 'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php', 'PhabricatorEdgeTypeTestCase' => 'infrastructure/edges/type/__tests__/PhabricatorEdgeTypeTestCase.php', + 'PhabricatorEditEngine' => 'applications/transactions/editengine/PhabricatorEditEngine.php', + 'PhabricatorEditEngineAPIMethod' => 'applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php', + 'PhabricatorEditEngineConfiguration' => 'applications/transactions/storage/PhabricatorEditEngineConfiguration.php', + 'PhabricatorEditEngineConfigurationEditController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php', + 'PhabricatorEditEngineConfigurationEditEngine' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php', + 'PhabricatorEditEngineConfigurationEditor' => 'applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php', + 'PhabricatorEditEngineConfigurationListController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php', + 'PhabricatorEditEngineConfigurationPHIDType' => 'applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php', + 'PhabricatorEditEngineConfigurationQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php', + 'PhabricatorEditEngineConfigurationSaveController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php', + 'PhabricatorEditEngineConfigurationSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php', + 'PhabricatorEditEngineConfigurationTransaction' => 'applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php', + 'PhabricatorEditEngineConfigurationTransactionQuery' => 'applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php', + 'PhabricatorEditEngineConfigurationViewController' => 'applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php', + 'PhabricatorEditEngineController' => 'applications/transactions/controller/PhabricatorEditEngineController.php', + 'PhabricatorEditEngineListController' => 'applications/transactions/controller/PhabricatorEditEngineListController.php', + 'PhabricatorEditEngineQuery' => 'applications/transactions/query/PhabricatorEditEngineQuery.php', + 'PhabricatorEditEngineSearchEngine' => 'applications/transactions/query/PhabricatorEditEngineSearchEngine.php', 'PhabricatorEditField' => 'applications/transactions/editfield/PhabricatorEditField.php', 'PhabricatorEditType' => 'applications/transactions/edittype/PhabricatorEditType.php', 'PhabricatorEditor' => 'infrastructure/PhabricatorEditor.php', @@ -2295,6 +2311,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php', 'PhabricatorInlineCommentPreviewController' => 'infrastructure/diff/PhabricatorInlineCommentPreviewController.php', 'PhabricatorInlineSummaryView' => 'infrastructure/diff/view/PhabricatorInlineSummaryView.php', + 'PhabricatorInstructionsEditField' => 'applications/transactions/editfield/PhabricatorInstructionsEditField.php', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementExtractWorkflow.php', 'PhabricatorInternationalizationManagementWorkflow' => 'infrastructure/internationalization/management/PhabricatorInternationalizationManagementWorkflow.php', 'PhabricatorInvalidConfigSetupCheck' => 'applications/config/check/PhabricatorInvalidConfigSetupCheck.php', @@ -2309,7 +2326,7 @@ phutil_register_library_map(array( 'PhabricatorLegalpadApplication' => 'applications/legalpad/application/PhabricatorLegalpadApplication.php', 'PhabricatorLegalpadConfigOptions' => 'applications/legalpad/config/PhabricatorLegalpadConfigOptions.php', 'PhabricatorLegalpadDocumentPHIDType' => 'applications/legalpad/phid/PhabricatorLegalpadDocumentPHIDType.php', - 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php', + 'PhabricatorLegalpadSignaturePolicyRule' => 'applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php', 'PhabricatorLibraryTestCase' => '__tests__/PhabricatorLibraryTestCase.php', 'PhabricatorLipsumArtist' => 'applications/lipsum/image/PhabricatorLipsumArtist.php', 'PhabricatorLipsumGenerateWorkflow' => 'applications/lipsum/management/PhabricatorLipsumGenerateWorkflow.php', @@ -2546,6 +2563,7 @@ phutil_register_library_map(array( 'PhabricatorOwnersPackageTransactionQuery' => 'applications/owners/query/PhabricatorOwnersPackageTransactionQuery.php', 'PhabricatorOwnersPath' => 'applications/owners/storage/PhabricatorOwnersPath.php', 'PhabricatorOwnersPathsController' => 'applications/owners/controller/PhabricatorOwnersPathsController.php', + 'PhabricatorOwnersSchemaSpec' => 'applications/owners/storage/PhabricatorOwnersSchemaSpec.php', 'PhabricatorOwnersSearchField' => 'applications/owners/searchfield/PhabricatorOwnersSearchField.php', 'PhabricatorPHDConfigOptions' => 'applications/config/option/PhabricatorPHDConfigOptions.php', 'PhabricatorPHID' => 'applications/phid/storage/PhabricatorPHID.php', @@ -2641,13 +2659,18 @@ phutil_register_library_map(array( 'PhabricatorPhrictionApplication' => 'applications/phriction/application/PhabricatorPhrictionApplication.php', 'PhabricatorPhrictionConfigOptions' => 'applications/phriction/config/PhabricatorPhrictionConfigOptions.php', 'PhabricatorPhurlApplication' => 'applications/phurl/application/PhabricatorPhurlApplication.php', + 'PhabricatorPhurlConfigOptions' => 'applications/config/option/PhabricatorPhurlConfigOptions.php', 'PhabricatorPhurlController' => 'applications/phurl/controller/PhabricatorPhurlController.php', 'PhabricatorPhurlDAO' => 'applications/phurl/storage/PhabricatorPhurlDAO.php', 'PhabricatorPhurlLinkRemarkupRule' => 'applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php', 'PhabricatorPhurlRemarkupRule' => 'applications/phurl/remarkup/PhabricatorPhurlRemarkupRule.php', 'PhabricatorPhurlSchemaSpec' => 'applications/phurl/storage/PhabricatorPhurlSchemaSpec.php', + 'PhabricatorPhurlShortURLController' => 'applications/phurl/controller/PhabricatorPhurlShortURLController.php', + 'PhabricatorPhurlShortURLDefaultController' => 'applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php', 'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php', 'PhabricatorPhurlURLAccessController' => 'applications/phurl/controller/PhabricatorPhurlURLAccessController.php', + 'PhabricatorPhurlURLCommentController' => 'applications/phurl/controller/PhabricatorPhurlURLCommentController.php', + 'PhabricatorPhurlURLCreateCapability' => 'applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php', 'PhabricatorPhurlURLEditController' => 'applications/phurl/controller/PhabricatorPhurlURLEditController.php', 'PhabricatorPhurlURLEditor' => 'applications/phurl/editor/PhabricatorPhurlURLEditor.php', 'PhabricatorPhurlURLListController' => 'applications/phurl/controller/PhabricatorPhurlURLListController.php', @@ -2767,7 +2790,7 @@ phutil_register_library_map(array( 'PhabricatorProjectViewController' => 'applications/project/controller/PhabricatorProjectViewController.php', 'PhabricatorProjectWatchController' => 'applications/project/controller/PhabricatorProjectWatchController.php', 'PhabricatorProjectsEditField' => 'applications/transactions/editfield/PhabricatorProjectsEditField.php', - 'PhabricatorProjectsPolicyRule' => 'applications/policy/rule/PhabricatorProjectsPolicyRule.php', + 'PhabricatorProjectsPolicyRule' => 'applications/project/policyrule/PhabricatorProjectsPolicyRule.php', 'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php', 'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php', 'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php', @@ -2954,6 +2977,7 @@ phutil_register_library_map(array( 'PhabricatorSetupIssue' => 'applications/config/issue/PhabricatorSetupIssue.php', 'PhabricatorSetupIssueUIExample' => 'applications/uiexample/examples/PhabricatorSetupIssueUIExample.php', 'PhabricatorSetupIssueView' => 'applications/config/view/PhabricatorSetupIssueView.php', + 'PhabricatorShortSite' => 'aphront/site/PhabricatorShortSite.php', 'PhabricatorSimpleEditType' => 'applications/transactions/edittype/PhabricatorSimpleEditType.php', 'PhabricatorSite' => 'aphront/site/PhabricatorSite.php', 'PhabricatorSlowvoteApplication' => 'applications/slowvote/application/PhabricatorSlowvoteApplication.php', @@ -3176,7 +3200,7 @@ phutil_register_library_map(array( 'PhabricatorUserTitleField' => 'applications/people/customfield/PhabricatorUserTitleField.php', 'PhabricatorUserTransaction' => 'applications/people/storage/PhabricatorUserTransaction.php', 'PhabricatorUsersEditField' => 'applications/transactions/editfield/PhabricatorUsersEditField.php', - 'PhabricatorUsersPolicyRule' => 'applications/policy/rule/PhabricatorUsersPolicyRule.php', + 'PhabricatorUsersPolicyRule' => 'applications/people/policyrule/PhabricatorUsersPolicyRule.php', 'PhabricatorUsersSearchField' => 'applications/people/searchfield/PhabricatorUsersSearchField.php', 'PhabricatorVCSResponse' => 'applications/repository/response/PhabricatorVCSResponse.php', 'PhabricatorVeryWowEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorVeryWowEnglishTranslation.php', @@ -3224,13 +3248,13 @@ phutil_register_library_map(array( 'PhabricatorWorkingCopyDiscoveryTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyDiscoveryTestCase.php', 'PhabricatorWorkingCopyPullTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyPullTestCase.php', 'PhabricatorWorkingCopyTestCase' => 'applications/repository/engine/__tests__/PhabricatorWorkingCopyTestCase.php', + 'PhabricatorXHPASTDAO' => 'applications/phpast/storage/PhabricatorXHPASTDAO.php', + 'PhabricatorXHPASTParseTree' => 'applications/phpast/storage/PhabricatorXHPASTParseTree.php', 'PhabricatorXHPASTViewController' => 'applications/phpast/controller/PhabricatorXHPASTViewController.php', - 'PhabricatorXHPASTViewDAO' => 'applications/phpast/storage/PhabricatorXHPASTViewDAO.php', 'PhabricatorXHPASTViewFrameController' => 'applications/phpast/controller/PhabricatorXHPASTViewFrameController.php', 'PhabricatorXHPASTViewFramesetController' => 'applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php', 'PhabricatorXHPASTViewInputController' => 'applications/phpast/controller/PhabricatorXHPASTViewInputController.php', 'PhabricatorXHPASTViewPanelController' => 'applications/phpast/controller/PhabricatorXHPASTViewPanelController.php', - 'PhabricatorXHPASTViewParseTree' => 'applications/phpast/storage/PhabricatorXHPASTViewParseTree.php', 'PhabricatorXHPASTViewRunController' => 'applications/phpast/controller/PhabricatorXHPASTViewRunController.php', 'PhabricatorXHPASTViewStreamController' => 'applications/phpast/controller/PhabricatorXHPASTViewStreamController.php', 'PhabricatorXHPASTViewTreeController' => 'applications/phpast/controller/PhabricatorXHPASTViewTreeController.php', @@ -3248,6 +3272,7 @@ phutil_register_library_map(array( 'PhameBasicTemplateBlogSkin' => 'applications/phame/skins/PhameBasicTemplateBlogSkin.php', 'PhameBlog' => 'applications/phame/storage/PhameBlog.php', 'PhameBlogController' => 'applications/phame/controller/blog/PhameBlogController.php', + 'PhameBlogCreateCapability' => 'applications/phame/capability/PhameBlogCreateCapability.php', 'PhameBlogDeleteController' => 'applications/phame/controller/blog/PhameBlogDeleteController.php', 'PhameBlogEditController' => 'applications/phame/controller/blog/PhameBlogEditController.php', 'PhameBlogEditor' => 'applications/phame/editor/PhameBlogEditor.php', @@ -3255,23 +3280,28 @@ phutil_register_library_map(array( 'PhameBlogListController' => 'applications/phame/controller/blog/PhameBlogListController.php', 'PhameBlogLiveController' => 'applications/phame/controller/blog/PhameBlogLiveController.php', 'PhameBlogQuery' => 'applications/phame/query/PhameBlogQuery.php', + 'PhameBlogReplyHandler' => 'applications/phame/mail/PhameBlogReplyHandler.php', 'PhameBlogSearchEngine' => 'applications/phame/query/PhameBlogSearchEngine.php', 'PhameBlogSite' => 'applications/phame/site/PhameBlogSite.php', 'PhameBlogSkin' => 'applications/phame/skins/PhameBlogSkin.php', 'PhameBlogTransaction' => 'applications/phame/storage/PhameBlogTransaction.php', + 'PhameBlogTransactionQuery' => 'applications/phame/query/PhameBlogTransactionQuery.php', 'PhameBlogViewController' => 'applications/phame/controller/blog/PhameBlogViewController.php', 'PhameCelerityResources' => 'applications/phame/celerity/PhameCelerityResources.php', 'PhameConduitAPIMethod' => 'applications/phame/conduit/PhameConduitAPIMethod.php', + 'PhameConstants' => 'applications/phame/constants/PhameConstants.php', 'PhameController' => 'applications/phame/controller/PhameController.php', 'PhameCreatePostConduitAPIMethod' => 'applications/phame/conduit/PhameCreatePostConduitAPIMethod.php', 'PhameDAO' => 'applications/phame/storage/PhameDAO.php', 'PhamePost' => 'applications/phame/storage/PhamePost.php', + 'PhamePostCommentController' => 'applications/phame/controller/post/PhamePostCommentController.php', 'PhamePostController' => 'applications/phame/controller/post/PhamePostController.php', 'PhamePostDeleteController' => 'applications/phame/controller/post/PhamePostDeleteController.php', 'PhamePostEditController' => 'applications/phame/controller/post/PhamePostEditController.php', 'PhamePostEditor' => 'applications/phame/editor/PhamePostEditor.php', 'PhamePostFramedController' => 'applications/phame/controller/post/PhamePostFramedController.php', 'PhamePostListController' => 'applications/phame/controller/post/PhamePostListController.php', + 'PhamePostMailReceiver' => 'applications/phame/mail/PhamePostMailReceiver.php', 'PhamePostNewController' => 'applications/phame/controller/post/PhamePostNewController.php', 'PhamePostNotLiveController' => 'applications/phame/controller/post/PhamePostNotLiveController.php', 'PhamePostPreviewController' => 'applications/phame/controller/post/PhamePostPreviewController.php', @@ -3280,6 +3310,7 @@ phutil_register_library_map(array( 'PhamePostReplyHandler' => 'applications/phame/mail/PhamePostReplyHandler.php', 'PhamePostSearchEngine' => 'applications/phame/query/PhamePostSearchEngine.php', 'PhamePostTransaction' => 'applications/phame/storage/PhamePostTransaction.php', + 'PhamePostTransactionComment' => 'applications/phame/storage/PhamePostTransactionComment.php', 'PhamePostTransactionQuery' => 'applications/phame/query/PhamePostTransactionQuery.php', 'PhamePostUnpublishController' => 'applications/phame/controller/post/PhamePostUnpublishController.php', 'PhamePostView' => 'applications/phame/view/PhamePostView.php', @@ -3400,7 +3431,6 @@ phutil_register_library_map(array( 'PhortuneMerchantTransactionQuery' => 'applications/phortune/query/PhortuneMerchantTransactionQuery.php', 'PhortuneMerchantViewController' => 'applications/phortune/controller/PhortuneMerchantViewController.php', 'PhortuneMonthYearExpiryControl' => 'applications/phortune/control/PhortuneMonthYearExpiryControl.php', - 'PhortuneNotImplementedException' => 'applications/phortune/exception/PhortuneNotImplementedException.php', 'PhortuneOrderTableView' => 'applications/phortune/view/PhortuneOrderTableView.php', 'PhortunePayPalPaymentProvider' => 'applications/phortune/provider/PhortunePayPalPaymentProvider.php', 'PhortunePaymentMethod' => 'applications/phortune/storage/PhortunePaymentMethod.php', @@ -3728,6 +3758,7 @@ phutil_register_library_map(array( 'PhabricatorCustomFieldInterface', 'PhabricatorApplicationTransactionInterface', 'AlmanacPropertyInterface', + 'PhabricatorDestructibleInterface', ), 'AlmanacBindingEditController' => 'AlmanacServiceController', 'AlmanacBindingEditor' => 'PhabricatorApplicationTransactionEditor', @@ -5045,6 +5076,7 @@ phutil_register_library_map(array( 'PhabricatorFlaggableInterface', 'PhabricatorPolicyInterface', 'PhabricatorDestructibleInterface', + 'PhabricatorSubscribableInterface', ), 'HeraldRuleController' => 'HeraldController', 'HeraldRuleEditor' => 'PhabricatorApplicationTransactionEditor', @@ -5490,7 +5522,7 @@ phutil_register_library_map(array( 'PasteCreateMailReceiver' => 'PhabricatorMailReceiver', 'PasteDefaultEditCapability' => 'PhabricatorPolicyCapability', 'PasteDefaultViewCapability' => 'PhabricatorPolicyCapability', - 'PasteEditConduitAPIMethod' => 'PhabricatorApplicationEditEngineAPIMethod', + 'PasteEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'PasteEmbedView' => 'AphrontView', 'PasteInfoConduitAPIMethod' => 'PasteConduitAPIMethod', 'PasteMailReceiver' => 'PhabricatorObjectMailReceiver', @@ -5534,8 +5566,6 @@ phutil_register_library_map(array( 'PhabricatorApplicationDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationEditController' => 'PhabricatorApplicationsController', - 'PhabricatorApplicationEditEngine' => 'Phobject', - 'PhabricatorApplicationEditEngineAPIMethod' => 'ConduitAPIMethod', 'PhabricatorApplicationEditHTTPParameterHelpView' => 'AphrontView', 'PhabricatorApplicationEmailCommandsController' => 'PhabricatorApplicationsController', 'PhabricatorApplicationLaunchView' => 'AphrontTagView', @@ -6051,6 +6081,7 @@ phutil_register_library_map(array( 'PhabricatorDaemonManagementStatusWorkflow' => 'PhabricatorDaemonManagementWorkflow', 'PhabricatorDaemonManagementStopWorkflow' => 'PhabricatorDaemonManagementWorkflow', 'PhabricatorDaemonManagementWorkflow' => 'PhabricatorManagementWorkflow', + 'PhabricatorDaemonOverseerModule' => 'PhutilDaemonOverseerModule', 'PhabricatorDaemonReference' => 'Phobject', 'PhabricatorDaemonTaskGarbageCollector' => 'PhabricatorGarbageCollector', 'PhabricatorDaemonTasksTableView' => 'AphrontView', @@ -6147,7 +6178,6 @@ phutil_register_library_map(array( 'PhabricatorDisabledUserController' => 'PhabricatorAuthController', 'PhabricatorDisplayPreferencesSettingsPanel' => 'PhabricatorSettingsPanel', 'PhabricatorDisqusAuthProvider' => 'PhabricatorOAuth2AuthProvider', - 'PhabricatorDisqusConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorDivinerApplication' => 'PhabricatorApplication', 'PhabricatorDoorkeeperApplication' => 'PhabricatorApplication', 'PhabricatorDraft' => 'PhabricatorDraftDAO', @@ -6163,6 +6193,31 @@ phutil_register_library_map(array( 'PhabricatorEdgeTestCase' => 'PhabricatorTestCase', 'PhabricatorEdgeType' => 'Phobject', 'PhabricatorEdgeTypeTestCase' => 'PhabricatorTestCase', + 'PhabricatorEditEngine' => array( + 'Phobject', + 'PhabricatorPolicyInterface', + ), + 'PhabricatorEditEngineAPIMethod' => 'ConduitAPIMethod', + 'PhabricatorEditEngineConfiguration' => array( + 'PhabricatorSearchDAO', + 'PhabricatorApplicationTransactionInterface', + 'PhabricatorPolicyInterface', + ), + 'PhabricatorEditEngineConfigurationEditController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationEditEngine' => 'PhabricatorEditEngine', + 'PhabricatorEditEngineConfigurationEditor' => 'PhabricatorApplicationTransactionEditor', + 'PhabricatorEditEngineConfigurationListController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationPHIDType' => 'PhabricatorPHIDType', + 'PhabricatorEditEngineConfigurationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorEditEngineConfigurationSaveController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineConfigurationSearchEngine' => 'PhabricatorApplicationSearchEngine', + 'PhabricatorEditEngineConfigurationTransaction' => 'PhabricatorApplicationTransaction', + 'PhabricatorEditEngineConfigurationTransactionQuery' => 'PhabricatorApplicationTransactionQuery', + 'PhabricatorEditEngineConfigurationViewController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineController' => 'PhabricatorApplicationTransactionController', + 'PhabricatorEditEngineListController' => 'PhabricatorEditEngineController', + 'PhabricatorEditEngineQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhabricatorEditEngineSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhabricatorEditField' => 'Phobject', 'PhabricatorEditType' => 'Phobject', 'PhabricatorEditor' => 'Phobject', @@ -6383,6 +6438,7 @@ phutil_register_library_map(array( 'PhabricatorInlineCommentInterface' => 'PhabricatorMarkupInterface', 'PhabricatorInlineCommentPreviewController' => 'PhabricatorController', 'PhabricatorInlineSummaryView' => 'AphrontView', + 'PhabricatorInstructionsEditField' => 'PhabricatorEditField', 'PhabricatorInternationalizationManagementExtractWorkflow' => 'PhabricatorInternationalizationManagementWorkflow', 'PhabricatorInternationalizationManagementWorkflow' => 'PhabricatorManagementWorkflow', 'PhabricatorInvalidConfigSetupCheck' => 'PhabricatorSetupCheck', @@ -6665,6 +6721,7 @@ phutil_register_library_map(array( 'PhabricatorOwnersPackageTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhabricatorOwnersPath' => 'PhabricatorOwnersDAO', 'PhabricatorOwnersPathsController' => 'PhabricatorOwnersController', + 'PhabricatorOwnersSchemaSpec' => 'PhabricatorConfigSchemaSpec', 'PhabricatorOwnersSearchField' => 'PhabricatorSearchTokenizerField', 'PhabricatorPHDConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPHID' => 'Phobject', @@ -6701,7 +6758,7 @@ phutil_register_library_map(array( 'PhabricatorPasteController' => 'PhabricatorController', 'PhabricatorPasteDAO' => 'PhabricatorLiskDAO', 'PhabricatorPasteEditController' => 'PhabricatorPasteController', - 'PhabricatorPasteEditEngine' => 'PhabricatorApplicationEditEngine', + 'PhabricatorPasteEditEngine' => 'PhabricatorEditEngine', 'PhabricatorPasteEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPasteListController' => 'PhabricatorPasteController', 'PhabricatorPastePastePHIDType' => 'PhabricatorPHIDType', @@ -6770,11 +6827,14 @@ phutil_register_library_map(array( 'PhabricatorPhrictionApplication' => 'PhabricatorApplication', 'PhabricatorPhrictionConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPhurlApplication' => 'PhabricatorApplication', + 'PhabricatorPhurlConfigOptions' => 'PhabricatorApplicationConfigOptions', 'PhabricatorPhurlController' => 'PhabricatorController', 'PhabricatorPhurlDAO' => 'PhabricatorLiskDAO', 'PhabricatorPhurlLinkRemarkupRule' => 'PhutilRemarkupRule', 'PhabricatorPhurlRemarkupRule' => 'PhabricatorObjectRemarkupRule', 'PhabricatorPhurlSchemaSpec' => 'PhabricatorConfigSchemaSpec', + 'PhabricatorPhurlShortURLController' => 'PhabricatorPhurlController', + 'PhabricatorPhurlShortURLDefaultController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURL' => array( 'PhabricatorPhurlDAO', 'PhabricatorPolicyInterface', @@ -6788,6 +6848,8 @@ phutil_register_library_map(array( 'PhabricatorSpacesInterface', ), 'PhabricatorPhurlURLAccessController' => 'PhabricatorPhurlController', + 'PhabricatorPhurlURLCommentController' => 'PhabricatorPhurlController', + 'PhabricatorPhurlURLCreateCapability' => 'PhabricatorPolicyCapability', 'PhabricatorPhurlURLEditController' => 'PhabricatorPhurlController', 'PhabricatorPhurlURLEditor' => 'PhabricatorApplicationTransactionEditor', 'PhabricatorPhurlURLListController' => 'PhabricatorPhurlController', @@ -7163,6 +7225,7 @@ phutil_register_library_map(array( 'PhabricatorSetupIssue' => 'Phobject', 'PhabricatorSetupIssueUIExample' => 'PhabricatorUIExample', 'PhabricatorSetupIssueView' => 'AphrontView', + 'PhabricatorShortSite' => 'PhabricatorSite', 'PhabricatorSimpleEditType' => 'PhabricatorEditType', 'PhabricatorSite' => 'AphrontSite', 'PhabricatorSlowvoteApplication' => 'PhabricatorApplication', @@ -7479,13 +7542,13 @@ phutil_register_library_map(array( 'PhabricatorWorkingCopyDiscoveryTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyPullTestCase' => 'PhabricatorWorkingCopyTestCase', 'PhabricatorWorkingCopyTestCase' => 'PhabricatorTestCase', + 'PhabricatorXHPASTDAO' => 'PhabricatorLiskDAO', + 'PhabricatorXHPASTParseTree' => 'PhabricatorXHPASTDAO', 'PhabricatorXHPASTViewController' => 'PhabricatorController', - 'PhabricatorXHPASTViewDAO' => 'PhabricatorLiskDAO', 'PhabricatorXHPASTViewFrameController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewFramesetController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewInputController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHPASTViewPanelController' => 'PhabricatorXHPASTViewController', - 'PhabricatorXHPASTViewParseTree' => 'PhabricatorXHPASTViewDAO', 'PhabricatorXHPASTViewRunController' => 'PhabricatorXHPASTViewController', 'PhabricatorXHPASTViewStreamController' => 'PhabricatorXHPASTViewPanelController', 'PhabricatorXHPASTViewTreeController' => 'PhabricatorXHPASTViewPanelController', @@ -7511,6 +7574,7 @@ phutil_register_library_map(array( 'PhabricatorApplicationTransactionInterface', ), 'PhameBlogController' => 'PhameController', + 'PhameBlogCreateCapability' => 'PhabricatorPolicyCapability', 'PhameBlogDeleteController' => 'PhameBlogController', 'PhameBlogEditController' => 'PhameBlogController', 'PhameBlogEditor' => 'PhabricatorApplicationTransactionEditor', @@ -7518,13 +7582,16 @@ phutil_register_library_map(array( 'PhameBlogListController' => 'PhameBlogController', 'PhameBlogLiveController' => 'PhameBlogController', 'PhameBlogQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', + 'PhameBlogReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhameBlogSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhameBlogSite' => 'PhameSite', 'PhameBlogSkin' => 'PhabricatorController', 'PhameBlogTransaction' => 'PhabricatorApplicationTransaction', + 'PhameBlogTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhameBlogViewController' => 'PhameBlogController', 'PhameCelerityResources' => 'CelerityResources', 'PhameConduitAPIMethod' => 'ConduitAPIMethod', + 'PhameConstants' => 'Phobject', 'PhameController' => 'PhabricatorController', 'PhameCreatePostConduitAPIMethod' => 'PhameConduitAPIMethod', 'PhameDAO' => 'PhabricatorLiskDAO', @@ -7538,12 +7605,14 @@ phutil_register_library_map(array( 'PhabricatorSubscribableInterface', 'PhabricatorTokenReceiverInterface', ), + 'PhamePostCommentController' => 'PhamePostController', 'PhamePostController' => 'PhameController', 'PhamePostDeleteController' => 'PhamePostController', 'PhamePostEditController' => 'PhamePostController', 'PhamePostEditor' => 'PhabricatorApplicationTransactionEditor', 'PhamePostFramedController' => 'PhamePostController', 'PhamePostListController' => 'PhamePostController', + 'PhamePostMailReceiver' => 'PhabricatorObjectMailReceiver', 'PhamePostNewController' => 'PhamePostController', 'PhamePostNotLiveController' => 'PhamePostController', 'PhamePostPreviewController' => 'PhamePostController', @@ -7552,6 +7621,7 @@ phutil_register_library_map(array( 'PhamePostReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler', 'PhamePostSearchEngine' => 'PhabricatorApplicationSearchEngine', 'PhamePostTransaction' => 'PhabricatorApplicationTransaction', + 'PhamePostTransactionComment' => 'PhabricatorApplicationTransactionComment', 'PhamePostTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhamePostUnpublishController' => 'PhamePostController', 'PhamePostView' => 'AphrontView', @@ -7708,7 +7778,6 @@ phutil_register_library_map(array( 'PhortuneMerchantTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 'PhortuneMerchantViewController' => 'PhortuneMerchantController', 'PhortuneMonthYearExpiryControl' => 'AphrontFormControl', - 'PhortuneNotImplementedException' => 'Exception', 'PhortuneOrderTableView' => 'AphrontView', 'PhortunePayPalPaymentProvider' => 'PhortunePaymentProvider', 'PhortunePaymentMethod' => array( diff --git a/src/aphront/site/PhabricatorShortSite.php b/src/aphront/site/PhabricatorShortSite.php new file mode 100644 index 0000000000..d4c36aecbe --- /dev/null +++ b/src/aphront/site/PhabricatorShortSite.php @@ -0,0 +1,44 @@ +getHost(); + + $uri = PhabricatorEnv::getEnvConfig('phurl.short-uri'); + if (!strlen($uri)) { + return null; + } + + $phurl_installed = PhabricatorApplication::isClassInstalled( + 'PhabricatorPhurlApplication'); + if (!$phurl_installed) { + return false; + } + + if ($this->isHostMatch($host, array($uri))) { + return new PhabricatorShortSite(); + } + + return null; + } + + public function getRoutingMaps() { + $app = PhabricatorApplication::getByClass('PhabricatorPhurlApplication'); + + $maps = array(); + $maps[] = $this->newRoutingMap() + ->setApplication($app) + ->setRoutes($app->getShortRoutes()); + return $maps; + } + +} diff --git a/src/applications/almanac/storage/AlmanacBinding.php b/src/applications/almanac/storage/AlmanacBinding.php index b6c3a35cca..8f56c86c1a 100644 --- a/src/applications/almanac/storage/AlmanacBinding.php +++ b/src/applications/almanac/storage/AlmanacBinding.php @@ -6,7 +6,8 @@ final class AlmanacBinding PhabricatorPolicyInterface, PhabricatorCustomFieldInterface, PhabricatorApplicationTransactionInterface, - AlmanacPropertyInterface { + AlmanacPropertyInterface, + PhabricatorDestructibleInterface { protected $servicePHID; protected $devicePHID; @@ -204,4 +205,14 @@ final class AlmanacBinding return $timeline; } +/* -( PhabricatorDestructibleInterface )----------------------------------- */ + + + public function destroyObjectPermanently( + PhabricatorDestructionEngine $engine) { + + $this->delete(); + } + + } diff --git a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php index c81cbe64aa..8bbbedc3c6 100644 --- a/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php +++ b/src/applications/auth/provider/PhabricatorJIRAAuthProvider.php @@ -77,6 +77,8 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { const PROPERTY_JIRA_URI = 'oauth1:jira:uri'; const PROPERTY_PUBLIC_KEY = 'oauth1:jira:key:public'; const PROPERTY_PRIVATE_KEY = 'oauth1:jira:key:private'; + const PROPERTY_REPORT_LINK = 'oauth1:jira:report:link'; + const PROPERTY_REPORT_COMMENT = 'oauth1:jira:report:comment'; public function readFormValuesFromProvider() { @@ -100,6 +102,10 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { return array( self::PROPERTY_JIRA_NAME => $name, self::PROPERTY_JIRA_URI => $request->getStr(self::PROPERTY_JIRA_URI), + self::PROPERTY_REPORT_LINK => + $request->getInt(self::PROPERTY_REPORT_LINK, 0), + self::PROPERTY_REPORT_COMMENT => + $request->getInt(self::PROPERTY_REPORT_COMMENT, 0), ); } @@ -175,6 +181,7 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { 'JIRA 5 or earlier.')); $is_setup = $this->isSetup(); + $viewer = $request->getViewer(); $e_required = $request->isFormPost() ? null : true; @@ -249,11 +256,40 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { id(new AphrontFormStaticControl()) ->setLabel(pht('Public Key')) ->setValue($pkey)); + + $form + ->appendRemarkupInstructions( + pht( + '= Integration Options = '."\n". + 'Configure how to record Revisions on JIRA tasks.'."\n\n". + 'Note you\'ll have to restart the daemons for this to take '. + 'effect.')) + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->addCheckbox( + self::PROPERTY_REPORT_LINK, + 1, + new PHUIRemarkupView( + $viewer, + pht( + 'Create **Issue Link** to the Revision, as an "implemented '. + 'in" relationship.')), + $this->shouldCreateJIRALink())) + ->appendChild( + id(new AphrontFormCheckboxControl()) + ->addCheckbox( + self::PROPERTY_REPORT_COMMENT, + 1, + new PHUIRemarkupView( + $viewer, + pht( + '**Post a comment** in the JIRA task, similar to the '. + 'emails Phabricator sends.')), + $this->shouldCreateJIRAComment())); } } - /** * JIRA uses a setup step to generate public/private keys. */ @@ -286,4 +322,14 @@ final class PhabricatorJIRAAuthProvider extends PhabricatorOAuth1AuthProvider { return $adapter->newJIRAFuture($path, $method, $params); } + public function shouldCreateJIRALink() { + $config = $this->getProviderConfig(); + return $config->getProperty(self::PROPERTY_REPORT_LINK, true); + } + + public function shouldCreateJIRAComment() { + $config = $this->getProviderConfig(); + return $config->getProperty(self::PROPERTY_REPORT_COMMENT, true); + } + } diff --git a/src/applications/base/PhabricatorApplication.php b/src/applications/base/PhabricatorApplication.php index 080062a2d9..8dce30290d 100644 --- a/src/applications/base/PhabricatorApplication.php +++ b/src/applications/base/PhabricatorApplication.php @@ -635,8 +635,12 @@ abstract class PhabricatorApplication return array(); } - protected function getEditRoutePattern($base) { + protected function getEditRoutePattern($base = null) { return $base.'(?:(?P[0-9]\d*)/)?(?:(?Pparameters)/)?'; } + protected function getQueryRoutePattern($base = null) { + return $base.'(?:query/(?P[^/]+)/)?'; + } + } diff --git a/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php b/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php index 560e0c95c4..27e716556e 100644 --- a/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php +++ b/src/applications/base/controller/__tests__/PhabricatorAccessControlTestCase.php @@ -56,7 +56,6 @@ final class PhabricatorAccessControlTestCase extends PhabricatorTestCase { $env->overrideEnvConfig('phabricator.base-uri', 'http://'.$host); $env->overrideEnvConfig('policy.allow-public', false); $env->overrideEnvConfig('auth.require-email-verification', false); - $env->overrideEnvConfig('auth.email-domains', array()); $env->overrideEnvConfig('security.require-multi-factor-auth', false); diff --git a/src/applications/calendar/application/PhabricatorCalendarApplication.php b/src/applications/calendar/application/PhabricatorCalendarApplication.php index b5bdfaa1eb..0197ec8f02 100644 --- a/src/applications/calendar/application/PhabricatorCalendarApplication.php +++ b/src/applications/calendar/application/PhabricatorCalendarApplication.php @@ -68,6 +68,15 @@ final class PhabricatorCalendarApplication extends PhabricatorApplication { ); } + public function getHelpDocumentationArticles(PhabricatorUser $viewer) { + return array( + array( + 'name' => pht('Calendar User Guide'), + 'href' => PhabricatorEnv::getDoclink('Calendar User Guide'), + ), + ); + } + public function getQuickCreateItems(PhabricatorUser $viewer) { $items = array(); diff --git a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php index d01946b370..dd5b361f71 100644 --- a/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php +++ b/src/applications/calendar/editor/PhabricatorCalendarEventEditor.php @@ -436,9 +436,9 @@ final class PhabricatorCalendarEventEditor $body = parent::buildMailBody($object, $xactions); if (strlen($description)) { - $body->addTextSection( + $body->addRemarkupSection( pht('EVENT DESCRIPTION'), - $object->getDescription()); + $description); } $body->addLinkSection( diff --git a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php index 9a982f49b6..04e8b6d05b 100644 --- a/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php +++ b/src/applications/conduit/method/ConduitQueryConduitAPIMethod.php @@ -19,20 +19,20 @@ final class ConduitQueryConduitAPIMethod extends ConduitAPIMethod { } protected function execute(ConduitAPIRequest $request) { - $classes = id(new PhutilClassMapQuery()) - ->setAncestorClass('ConduitAPIMethod') + $methods = id(new PhabricatorConduitMethodQuery()) + ->setViewer($request->getUser()) ->execute(); - $names_to_params = array(); - foreach ($classes as $class) { - $names_to_params[$class->getAPIMethodName()] = array( - 'description' => $class->getMethodDescription(), - 'params' => $class->getParamTypes(), - 'return' => $class->getReturnType(), + $map = array(); + foreach ($methods as $method) { + $map[$method->getAPIMethodName()] = array( + 'description' => $method->getMethodDescription(), + 'params' => $method->getParamTypes(), + 'return' => $method->getReturnType(), ); } - return $names_to_params; + return $map; } } diff --git a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php index e1e7e1914b..cb30bf5e55 100644 --- a/src/applications/conduit/query/PhabricatorConduitMethodQuery.php +++ b/src/applications/conduit/query/PhabricatorConduitMethodQuery.php @@ -115,6 +115,43 @@ final class PhabricatorConduitMethodQuery return $methods; } + protected function willFilterPage(array $methods) { + $application_phids = array(); + foreach ($methods as $method) { + $application = $method->getApplication(); + if ($application === null) { + continue; + } + $application_phids[] = $application->getPHID(); + } + + if ($application_phids) { + $applications = id(new PhabricatorApplicationQuery()) + ->setParentQuery($this) + ->setViewer($this->getViewer()) + ->withPHIDs($application_phids) + ->execute(); + $applications = mpull($applications, null, 'getPHID'); + } else { + $applications = array(); + } + + // Remove methods which belong to an application the viewer can not see. + foreach ($methods as $key => $method) { + $application = $method->getApplication(); + if ($application === null) { + continue; + } + + if (empty($applications[$application->getPHID()])) { + $this->didRejectResult($method); + unset($methods[$key]); + } + } + + return $methods; + } + public function getQueryApplicationClass() { return 'PhabricatorConduitApplication'; } diff --git a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php index a89590cc18..0b2bd8614e 100644 --- a/src/applications/config/check/PhabricatorDaemonsSetupCheck.php +++ b/src/applications/config/check/PhabricatorDaemonsSetupCheck.php @@ -47,7 +47,6 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { } $phd_user = PhabricatorEnv::getEnvConfig('phd.user'); - $environment_hash = PhabricatorEnv::calculateEnvironmentHash(); $all_daemons = id(new PhabricatorDaemonLogQuery()) ->setViewer(PhabricatorUser::getOmnipotentUser()) ->withStatus(PhabricatorDaemonLogQuery::STATUS_ALIVE) @@ -91,107 +90,6 @@ final class PhabricatorDaemonsSetupCheck extends PhabricatorSetupCheck { ->addCommand('phabricator/ $ ./bin/phd restart'); } } - - if ($daemon->getEnvHash() != $environment_hash) { - $doc_href = PhabricatorEnv::getDocLink( - 'Managing Daemons with phd'); - - $summary = pht( - 'At least one daemon is currently running with different '. - 'configuration than the Phabricator web application.'); - - $list_section = null; - $env_info = $daemon->getEnvInfo(); - if ($env_info) { - $issues = PhabricatorEnv::compareEnvironmentInfo( - PhabricatorEnv::calculateEnvironmentInfo(), - $env_info); - - if ($issues) { - foreach ($issues as $key => $issue) { - $issues[$key] = phutil_tag('li', array(), $issue); - } - - $list_section = array( - pht( - 'The configurations differ in the following %s way(s):', - phutil_count($issues)), - phutil_tag( - 'ul', - array(), - $issues), - ); - } - } - - - $message = pht( - 'At least one daemon is currently running with a different '. - 'configuration (config checksum %s) than the web application '. - '(config checksum %s).'. - "\n\n%s". - 'This usually means that you have just made a configuration change '. - 'from the web UI, but have not yet restarted the daemons. You '. - 'need to restart the daemons after making configuration changes '. - 'so they will pick up the new values: until you do, they will '. - 'continue operating with the old settings.'. - "\n\n". - '(If you plan to make more changes, you can restart the daemons '. - 'once after you finish making all of your changes.)'. - "\n\n". - 'Use %s to restart daemons. You can find a list of running daemons '. - 'in the %s, which will also help you identify which daemon (or '. - 'daemons) have divergent configuration. For more information about '. - 'managing the daemons, see %s in the documentation.'. - "\n\n". - 'This can also happen if you use the %s environmental variable to '. - 'choose a configuration file, but the daemons run with a different '. - 'value than the web application. If restarting the daemons does '. - 'not resolve this issue and you use %s to select configuration, '. - 'check that it is set consistently.'. - "\n\n". - 'A third possible cause is that you run several machines, and '. - 'the %s configuration file differs between them. This file is '. - 'updated when you edit configuration from the CLI with %s. If '. - 'restarting the daemons does not resolve this issue and you '. - 'run multiple machines, check that all machines have identical '. - '%s configuration files.'. - "\n\n". - 'This issue is not severe, but usually indicates that something '. - 'is not configured the way you expect, and may cause the daemons '. - 'to exhibit different behavior than the web application does.', - - phutil_tag('tt', array(), substr($daemon->getEnvHash(), 0, 12)), - phutil_tag('tt', array(), substr($environment_hash, 0, 12)), - $list_section, - phutil_tag('tt', array(), 'bin/phd restart'), - phutil_tag( - 'a', - array( - 'href' => '/daemon/', - 'target' => '_blank', - ), - pht('Daemon Console')), - phutil_tag( - 'a', - array( - 'href' => $doc_href, - 'target' => '_blank', - ), - pht('Managing Daemons with phd')), - phutil_tag('tt', array(), 'PHABRICATOR_ENV'), - phutil_tag('tt', array(), 'PHABRICATOR_ENV'), - phutil_tag('tt', array(), 'phabricator/conf/local/local.json'), - phutil_tag('tt', array(), 'bin/config'), - phutil_tag('tt', array(), 'phabricator/conf/local/local.json')); - - $this->newIssue('daemons.need-restarting') - ->setName(pht('Daemons and Web Have Different Config')) - ->setSummary($summary) - ->setMessage($message) - ->addCommand('phabricator/ $ ./bin/phd restart'); - break; - } } } diff --git a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php index 92cafb9449..de087096fb 100644 --- a/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php +++ b/src/applications/config/check/PhabricatorExtraConfigSetupCheck.php @@ -292,6 +292,10 @@ final class PhabricatorExtraConfigSetupCheck extends PhabricatorSetupCheck { 'gcdaemon.ttl.task-archive' => $gc_reason, 'gcdaemon.ttl.general-cache' => $gc_reason, 'gcdaemon.ttl.conduit-logs' => $gc_reason, + + 'phd.variant-config' => pht( + 'This configuration is no longer relevant because daemons '. + 'restart automatically on configuration changes.'), ); return $ancient_config; diff --git a/src/applications/config/option/PhabricatorDisqusConfigOptions.php b/src/applications/config/option/PhabricatorDisqusConfigOptions.php deleted file mode 100644 index 9bc6e13e39..0000000000 --- a/src/applications/config/option/PhabricatorDisqusConfigOptions.php +++ /dev/null @@ -1,35 +0,0 @@ -newOption('disqus.shortname', 'string', null) - ->setSummary(pht('Shortname for Disqus comment widget.')) - ->setDescription( - pht( - "Website shortname to use for Disqus comment widget in Phame. ". - "For more information, see:\n\n". - "[[http://docs.disqus.com/help/4/ | Disqus Quick Start Guide]]\n". - "[[http://docs.disqus.com/help/68/ | Information on Shortnames]]")), - ); - } - -} diff --git a/src/applications/config/option/PhabricatorPHDConfigOptions.php b/src/applications/config/option/PhabricatorPHDConfigOptions.php index 587194cd4d..12c29da616 100644 --- a/src/applications/config/option/PhabricatorPHDConfigOptions.php +++ b/src/applications/config/option/PhabricatorPHDConfigOptions.php @@ -73,13 +73,6 @@ final class PhabricatorPHDConfigOptions "trace mode. See also '%s'.", 'phd debug', 'phd.verbose')), - $this->newOption('phd.variant-config', 'list', array()) - ->setDescription( - pht( - 'Specify config keys that can safely vary between the web tier '. - 'and the daemons. Primarily, this is a way to suppress the '. - '"Daemons and Web Have Different Config" setup issue on a per '. - 'config key basis.')), $this->newOption('phd.garbage-collection', 'wild', array()) ->setLocked(true) ->setLockedMessage( diff --git a/src/applications/config/option/PhabricatorPhurlConfigOptions.php b/src/applications/config/option/PhabricatorPhurlConfigOptions.php new file mode 100644 index 0000000000..726540d3dd --- /dev/null +++ b/src/applications/config/option/PhabricatorPhurlConfigOptions.php @@ -0,0 +1,35 @@ +newOption('phurl.short-uri', 'string', null) + ->setLocked(true) + ->setSummary(pht('URI that Phurl will use to shorten URLs.')) + ->setDescription( + pht( + 'Set the URI that Phurl will use to share shortened URLs.')) + ->addExample( + 'https://some-very-short-domain.museum/', + pht('Valid Setting')), + ); + } +} diff --git a/src/applications/conpherence/editor/ConpherenceEditor.php b/src/applications/conpherence/editor/ConpherenceEditor.php index 299b799d3e..7d3aa02d2f 100644 --- a/src/applications/conpherence/editor/ConpherenceEditor.php +++ b/src/applications/conpherence/editor/ConpherenceEditor.php @@ -623,9 +623,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { switch ($xaction->getTransactionType()) { case ConpherenceTransaction::TYPE_PICTURE: - return array($xaction->getNewValue()->getPHID()); case ConpherenceTransaction::TYPE_PICTURE_CROP: - return array($xaction->getNewValue()); + return array($xaction->getNewValue()); } return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); diff --git a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php index 60bf290ae5..32af8f6f13 100644 --- a/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php +++ b/src/applications/daemon/controller/PhabricatorDaemonLogViewController.php @@ -58,14 +58,6 @@ final class PhabricatorDaemonLogViewController } $header->addTag($tag); - $env_hash = PhabricatorEnv::calculateEnvironmentHash(); - if ($log->getEnvHash() != $env_hash) { - $tag = id(new PHUITagView()) - ->setType(PHUITagView::TYPE_STATE) - ->setBackgroundColor(PHUITagView::COLOR_YELLOW) - ->setName(pht('Stale Config')); - $header->addTag($tag); - } $properties = $this->buildPropertyListView($log); diff --git a/src/applications/daemon/event/PhabricatorDaemonEventListener.php b/src/applications/daemon/event/PhabricatorDaemonEventListener.php index db6d9a2eff..41324cc9c9 100644 --- a/src/applications/daemon/event/PhabricatorDaemonEventListener.php +++ b/src/applications/daemon/event/PhabricatorDaemonEventListener.php @@ -42,8 +42,6 @@ final class PhabricatorDaemonEventListener extends PhabricatorEventListener { ->setHost(php_uname('n')) ->setPID(getmypid()) ->setRunningAsUser($current_user['name']) - ->setEnvHash(PhabricatorEnv::calculateEnvironmentHash()) - ->setEnvInfo(PhabricatorEnv::calculateEnvironmentInfo()) ->setStatus(PhabricatorDaemonLog::STATUS_RUNNING) ->setArgv($event->getValue('argv')) ->setExplicitArgv($event->getValue('explicitArgv')) diff --git a/src/applications/daemon/storage/PhabricatorDaemonLog.php b/src/applications/daemon/storage/PhabricatorDaemonLog.php index 6573ea8e4c..2d005e33e0 100644 --- a/src/applications/daemon/storage/PhabricatorDaemonLog.php +++ b/src/applications/daemon/storage/PhabricatorDaemonLog.php @@ -17,8 +17,6 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO protected $runningAsUser; protected $argv; protected $explicitArgv = array(); - protected $envHash; - protected $envInfo; protected $status; protected function getConfiguration() { @@ -26,14 +24,12 @@ final class PhabricatorDaemonLog extends PhabricatorDaemonDAO self::CONFIG_SERIALIZATION => array( 'argv' => self::SERIALIZATION_JSON, 'explicitArgv' => self::SERIALIZATION_JSON, - 'envInfo' => self::SERIALIZATION_JSON, ), self::CONFIG_COLUMN_SCHEMA => array( 'daemon' => 'text255', 'host' => 'text255', 'pid' => 'uint32', 'runningAsUser' => 'text255?', - 'envHash' => 'bytes40', 'status' => 'text8', 'daemonID' => 'text64', ), diff --git a/src/applications/daemon/view/PhabricatorDaemonLogListView.php b/src/applications/daemon/view/PhabricatorDaemonLogListView.php index 8be2743bd1..6c96509505 100644 --- a/src/applications/daemon/view/PhabricatorDaemonLogListView.php +++ b/src/applications/daemon/view/PhabricatorDaemonLogListView.php @@ -17,7 +17,6 @@ final class PhabricatorDaemonLogListView extends AphrontView { throw new PhutilInvalidStateException('setUser'); } - $env_hash = PhabricatorEnv::calculateEnvironmentHash(); $list = new PHUIObjectItemListView(); $list->setFlush(true); foreach ($this->daemonLogs as $log) { @@ -33,15 +32,8 @@ final class PhabricatorDaemonLogListView extends AphrontView { $status = $log->getStatus(); switch ($status) { case PhabricatorDaemonLog::STATUS_RUNNING: - if ($env_hash != $log->getEnvHash()) { - $item->setStatusIcon('fa-warning yellow'); - $item->addAttribute(pht( - 'This daemon is running with an out of date configuration and '. - 'should be restarted.')); - } else { - $item->setStatusIcon('fa-rocket green'); - $item->addAttribute(pht('This daemon is running.')); - } + $item->setStatusIcon('fa-rocket green'); + $item->addAttribute(pht('This daemon is running.')); break; case PhabricatorDaemonLog::STATUS_DEAD: $item->setStatusIcon('fa-warning red'); diff --git a/src/applications/differential/controller/DifferentialChangesetViewController.php b/src/applications/differential/controller/DifferentialChangesetViewController.php index a73875c8f8..3f08ce4883 100644 --- a/src/applications/differential/controller/DifferentialChangesetViewController.php +++ b/src/applications/differential/controller/DifferentialChangesetViewController.php @@ -387,7 +387,6 @@ final class DifferentialChangesetViewController extends DifferentialController { $inlines = array(); foreach ($messages as $message) { $description = $message->getProperty('description'); - $description = '%%%'.$description.'%%%'; $inlines[] = id(clone $template) ->setSyntheticAuthor(pht('Lint: %s', $message->getName())) diff --git a/src/applications/differential/editor/DifferentialTransactionEditor.php b/src/applications/differential/editor/DifferentialTransactionEditor.php index 2a8e907145..fabd2511ba 100644 --- a/src/applications/differential/editor/DifferentialTransactionEditor.php +++ b/src/applications/differential/editor/DifferentialTransactionEditor.php @@ -1298,15 +1298,6 @@ final class DifferentialTransactionEditor return true; } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) {} - - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - } - protected function expandCustomRemarkupBlockTransactions( PhabricatorLiskDAO $object, array $xactions, diff --git a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php index f356da3a10..be0d2c4faa 100644 --- a/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php +++ b/src/applications/diffusion/conduit/DiffusionUpdateCoverageConduitAPIMethod.php @@ -20,11 +20,17 @@ final class DiffusionUpdateCoverageConduitAPIMethod } protected function defineParamTypes() { + $modes = array( + 'overwrite', + 'update', + ); + return array( 'repositoryPHID' => 'required phid', 'branch' => 'required string', 'commit' => 'required string', 'coverage' => 'required map', + 'mode' => 'optional '.$this->formatStringConstants($modes), ); } @@ -77,16 +83,31 @@ final class DiffusionUpdateCoverageConduitAPIMethod $table_name = 'repository_coverage'; $conn->openTransaction(); - queryfx( - $conn, - 'DELETE FROM %T WHERE branchID = %d', - $table_name, - $branch->getID()); + $mode = $request->getValue('mode'); + switch ($mode) { + case '': + case 'overwrite': + // sets the coverage for the whole branch, deleting all previous + // coverage information + queryfx( + $conn, + 'DELETE FROM %T WHERE branchID = %d', + $table_name, + $branch->getID()); + break; + case 'update': + // sets the coverage for the provided files on the specified commit + break; + default: + $conn->killTransaction(); + throw new Exception(pht('Invalid mode "%s".', $mode)); + } foreach (PhabricatorLiskDAO::chunkSQL($sql) as $chunk) { queryfx( $conn, - 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q', + 'INSERT INTO %T (branchID, pathID, commitID, coverage) VALUES %Q'. + ' ON DUPLICATE KEY UPDATE coverage=VALUES(coverage)', $table_name, $chunk); } diff --git a/src/applications/diffusion/view/DiffusionBrowseTableView.php b/src/applications/diffusion/view/DiffusionBrowseTableView.php index 71cc659897..6354c5dc5e 100644 --- a/src/applications/diffusion/view/DiffusionBrowseTableView.php +++ b/src/applications/diffusion/view/DiffusionBrowseTableView.php @@ -30,8 +30,7 @@ final class DiffusionBrowseTableView extends DiffusionView { $rows = array(); $show_edit = false; foreach ($this->paths as $path) { - - $history_link = $this->linkHistory($path->getPath()); + $full_path = $base_path.$path->getPath(); $dir_slash = null; $file_type = $path->getFileType(); @@ -40,11 +39,13 @@ final class DiffusionBrowseTableView extends DiffusionView { $dir_slash = '/'; $browse_link = phutil_tag('strong', array(), $this->linkBrowse( - $base_path.$path->getPath().$dir_slash, + $full_path.$dir_slash, array( 'type' => $file_type, 'name' => $browse_text, ))); + + $history_path = $full_path.'/'; } else if ($file_type == DifferentialChangeType::FILE_SUBMODULE) { $browse_text = $path->getPath().'/'; $browse_link = phutil_tag('strong', array(), $this->linkBrowse( @@ -55,16 +56,22 @@ final class DiffusionBrowseTableView extends DiffusionView { 'hash' => $path->getHash(), 'external' => $path->getExternalURI(), ))); + + $history_path = $full_path.'/'; } else { $browse_text = $path->getPath(); $browse_link = $this->linkBrowse( - $base_path.$path->getPath(), + $full_path, array( 'type' => $file_type, 'name' => $browse_text, )); + + $history_path = $full_path; } + $history_link = $this->linkHistory($history_path); + $dict = array( 'lint' => celerity_generate_unique_node_id(), 'commit' => celerity_generate_unique_node_id(), @@ -73,7 +80,7 @@ final class DiffusionBrowseTableView extends DiffusionView { 'details' => celerity_generate_unique_node_id(), ); - $need_pull[$base_path.$path->getPath().$dir_slash] = $dict; + $need_pull[$full_path.$dir_slash] = $dict; foreach ($dict as $k => $uniq) { $dict[$k] = phutil_tag('span', array('id' => $uniq), ''); } diff --git a/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php b/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php index 7c6f15cf04..24631ed1b3 100644 --- a/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php +++ b/src/applications/doorkeeper/worker/DoorkeeperJIRAFeedWorker.php @@ -40,6 +40,14 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { return; } + $do_anything = ($this->shouldPostComment() || $this->shouldPostLink()); + if (!$do_anything) { + $this->log( + "%s\n", + pht('JIRA integration is configured not to post anything.')); + return; + } + $xobjs = id(new DoorkeeperExternalObjectQuery()) ->setViewer($viewer) ->withPHIDs($jira_issue_phids) @@ -60,7 +68,6 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { return; } - $story_text = $this->renderStoryText(); $xobjs = mgroup($xobjs, 'getApplicationDomain'); foreach ($xobjs as $domain => $xobj_list) { @@ -84,13 +91,16 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { foreach ($xobj_list as $xobj) { foreach ($accounts as $account) { try { - $provider->newJIRAFuture( - $account, - 'rest/api/2/issue/'.$xobj->getObjectID().'/comment', - 'POST', - array( - 'body' => $story_text, - ))->resolveJSON(); + $jira_key = $xobj->getObjectID(); + + if ($this->shouldPostComment()) { + $this->postComment($account, $jira_key); + } + + if ($this->shouldPostLink()) { + $this->postLink($account, $jira_key); + } + break; } catch (HTTPFutureResponseStatus $ex) { phlog($ex); @@ -169,14 +179,70 @@ final class DoorkeeperJIRAFeedWorker extends DoorkeeperFeedWorker { return $try_users; } + private function shouldPostComment() { + return $this->getProvider()->shouldCreateJIRAComment(); + } + + private function shouldPostLink() { + return $this->getProvider()->shouldCreateJIRALink(); + } + + private function postComment($account, $jira_key) { + $provider = $this->getProvider(); + + $provider->newJIRAFuture( + $account, + 'rest/api/2/issue/'.$jira_key.'/comment', + 'POST', + array( + 'body' => $this->renderStoryText(), + ))->resolveJSON(); + } + private function renderStoryText() { $object = $this->getStoryObject(); $publisher = $this->getPublisher(); $text = $publisher->getStoryText($object); - $uri = $publisher->getObjectURI($object); - return $text."\n\n".$uri; + if ($this->shouldPostLink()) { + return $text; + } else { + // include the link in the comment + return $text."\n\n".$publisher->getObjectURI($object); + } } + private function postLink($account, $jira_key) { + $provider = $this->getProvider(); + $object = $this->getStoryObject(); + $publisher = $this->getPublisher(); + $icon_uri = celerity_get_resource_uri('rsrc/favicons/favicon-16x16.png'); + + $provider->newJIRAFuture( + $account, + 'rest/api/2/issue/'.$jira_key.'/remotelink', + 'POST', + + // format documented at http://bit.ly/1K5T0Li + array( + 'globalId' => $object->getPHID(), + 'application' => array( + 'type' => 'com.phacility.phabricator', + 'name' => 'Phabricator', + ), + 'relationship' => 'implemented in', + 'object' => array( + 'url' => $publisher->getObjectURI($object), + 'title' => $publisher->getObjectTitle($object), + 'icon' => array( + 'url16x16' => $icon_uri, + 'title' => 'Phabricator', + ), + 'status' => array( + 'resolved' => $publisher->isObjectClosed($object), + ), + ), + ))->resolveJSON(); + } } diff --git a/src/applications/herald/application/PhabricatorHeraldApplication.php b/src/applications/herald/application/PhabricatorHeraldApplication.php index e3537d9722..bef94b6ac4 100644 --- a/src/applications/herald/application/PhabricatorHeraldApplication.php +++ b/src/applications/herald/application/PhabricatorHeraldApplication.php @@ -47,10 +47,10 @@ final class PhabricatorHeraldApplication extends PhabricatorApplication { public function getRoutes() { return array( + '/H(?P[1-9]\d*)' => 'HeraldRuleViewController', '/herald/' => array( '(?:query/(?P[^/]+)/)?' => 'HeraldRuleListController', 'new/' => 'HeraldNewController', - 'rule/(?P[1-9]\d*)/' => 'HeraldRuleViewController', 'edit/(?:(?P[1-9]\d*)/)?' => 'HeraldRuleController', 'disable/(?P[1-9]\d*)/(?P\w+)/' => 'HeraldDisableController', diff --git a/src/applications/herald/controller/HeraldDisableController.php b/src/applications/herald/controller/HeraldDisableController.php index 054f30e7d3..bdbefa55ea 100644 --- a/src/applications/herald/controller/HeraldDisableController.php +++ b/src/applications/herald/controller/HeraldDisableController.php @@ -25,7 +25,7 @@ final class HeraldDisableController extends HeraldController { HeraldManageGlobalRulesCapability::CAPABILITY); } - $view_uri = $this->getApplicationURI("rule/{$id}/"); + $view_uri = '/'.$rule->getMonogram(); $is_disable = ($action === 'disable'); diff --git a/src/applications/herald/controller/HeraldRuleController.php b/src/applications/herald/controller/HeraldRuleController.php index 37a583c7ef..510113b0de 100644 --- a/src/applications/herald/controller/HeraldRuleController.php +++ b/src/applications/herald/controller/HeraldRuleController.php @@ -22,7 +22,7 @@ final class HeraldRuleController extends HeraldController { if (!$rule) { return new Aphront404Response(); } - $cancel_uri = $this->getApplicationURI("rule/{$id}/"); + $cancel_uri = '/'.$rule->getMonogram(); } else { $new_uri = $this->getApplicationURI('new/'); @@ -128,7 +128,7 @@ final class HeraldRuleController extends HeraldController { list($e_name, $errors) = $this->saveRule($adapter, $rule, $request); if (!$errors) { $id = $rule->getID(); - $uri = $this->getApplicationURI("rule/{$id}/"); + $uri = '/'.$rule->getMonogram(); return id(new AphrontRedirectResponse())->setURI($uri); } } diff --git a/src/applications/herald/controller/HeraldRuleViewController.php b/src/applications/herald/controller/HeraldRuleViewController.php index f6ec235a9e..2063ebcc2f 100644 --- a/src/applications/herald/controller/HeraldRuleViewController.php +++ b/src/applications/herald/controller/HeraldRuleViewController.php @@ -67,7 +67,7 @@ final class HeraldRuleViewController extends HeraldController { $view = id(new PhabricatorActionListView()) ->setUser($viewer) ->setObject($rule) - ->setObjectURI($this->getApplicationURI("rule/{$id}/")); + ->setObjectURI('/'.$rule->getMonogram()); $can_edit = PhabricatorPolicyFilter::hasCapability( $viewer, diff --git a/src/applications/herald/phid/HeraldRulePHIDType.php b/src/applications/herald/phid/HeraldRulePHIDType.php index f8d5c25db4..34c6d80c5f 100644 --- a/src/applications/herald/phid/HeraldRulePHIDType.php +++ b/src/applications/herald/phid/HeraldRulePHIDType.php @@ -32,12 +32,12 @@ final class HeraldRulePHIDType extends PhabricatorPHIDType { foreach ($handles as $phid => $handle) { $rule = $objects[$phid]; - $id = $rule->getID(); - $name = $rule->getName(); + $monogram = $rule->getMonogram(); + $name = $rule->getName(); - $handle->setName("H{$id}"); - $handle->setFullName("H{$id} {$name}"); - $handle->setURI("/herald/rule/{$id}/"); + $handle->setName($monogram); + $handle->setFullName("{$monogram} {$name}"); + $handle->setURI("/{$monogram}"); } } diff --git a/src/applications/herald/query/HeraldRuleSearchEngine.php b/src/applications/herald/query/HeraldRuleSearchEngine.php index 04b7a5d852..ad4c9a7ad2 100644 --- a/src/applications/herald/query/HeraldRuleSearchEngine.php +++ b/src/applications/herald/query/HeraldRuleSearchEngine.php @@ -173,12 +173,12 @@ final class HeraldRuleSearchEngine extends PhabricatorApplicationSearchEngine { $list = id(new PHUIObjectItemListView()) ->setUser($viewer); foreach ($rules as $rule) { - $id = $rule->getID(); + $monogram = $rule->getMonogram(); $item = id(new PHUIObjectItemView()) - ->setObjectName("H{$id}") + ->setObjectName($monogram) ->setHeader($rule->getName()) - ->setHref($this->getApplicationURI("rule/{$id}/")); + ->setHref("/{$monogram}"); if ($rule->isPersonalRule()) { $item->addIcon('fa-user', pht('Personal Rule')); diff --git a/src/applications/herald/storage/HeraldRule.php b/src/applications/herald/storage/HeraldRule.php index efdfcdece6..51707fcc8a 100644 --- a/src/applications/herald/storage/HeraldRule.php +++ b/src/applications/herald/storage/HeraldRule.php @@ -5,7 +5,8 @@ final class HeraldRule extends HeraldDAO PhabricatorApplicationTransactionInterface, PhabricatorFlaggableInterface, PhabricatorPolicyInterface, - PhabricatorDestructibleInterface { + PhabricatorDestructibleInterface, + PhabricatorSubscribableInterface { const TABLE_RULE_APPLIED = 'herald_ruleapplied'; @@ -320,8 +321,25 @@ final class HeraldRule extends HeraldDAO } +/* -( PhabricatorSubscribableInterface )----------------------------------- */ + + + public function isAutomaticallySubscribed($phid) { + return $this->isPersonalRule() && $phid == $this->getAuthorPHID(); + } + + public function shouldShowSubscribersProperty() { + return true; + } + + public function shouldAllowSubscription($phid) { + return true; + } + + /* -( PhabricatorDestructibleInterface )----------------------------------- */ + public function destroyObjectPermanently( PhabricatorDestructionEngine $engine) { diff --git a/src/applications/herald/storage/HeraldSchemaSpec.php b/src/applications/herald/storage/HeraldSchemaSpec.php index 33045634d7..1b7b574296 100644 --- a/src/applications/herald/storage/HeraldSchemaSpec.php +++ b/src/applications/herald/storage/HeraldSchemaSpec.php @@ -33,6 +33,7 @@ final class HeraldSchemaSpec extends PhabricatorConfigSchemaSpec { 'unique' => true, ), )); + $this->buildEdgeSchemata(new HeraldRule()); } } diff --git a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php b/src/applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php similarity index 99% rename from src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php rename to src/applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php index 3aa7ef8d9c..423b19e510 100644 --- a/src/applications/policy/rule/PhabricatorLegalpadSignaturePolicyRule.php +++ b/src/applications/legalpad/policyrule/PhabricatorLegalpadSignaturePolicyRule.php @@ -66,7 +66,6 @@ final class PhabricatorLegalpadSignaturePolicyRule ->setViewer($viewer) ->withPHIDs($value) ->execute(); - return mpull($handles, 'getFullName', 'getPHID'); } diff --git a/src/applications/maniphest/storage/ManiphestTransaction.php b/src/applications/maniphest/storage/ManiphestTransaction.php index 0456673a10..024a292337 100644 --- a/src/applications/maniphest/storage/ManiphestTransaction.php +++ b/src/applications/maniphest/storage/ManiphestTransaction.php @@ -839,9 +839,9 @@ final class ManiphestTransaction case self::TYPE_MERGED_FROM: return pht( - '%s merged %d task(s) %s into %s.', + '%s merged %s task(s) %s into %s.', $this->renderHandleLink($author_phid), - count($new), + phutil_count($new), $this->renderHandleList($new), $this->renderHandleLink($object_phid)); diff --git a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php index 81f8ade37b..52a9a78a19 100644 --- a/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php +++ b/src/applications/metamta/receiver/PhabricatorObjectMailReceiver.php @@ -126,7 +126,7 @@ abstract class PhabricatorObjectMailReceiver extends PhabricatorMailReceiver { $expect_hash = self::computeMailHash($object->getMailKey(), $check_phid); - if ($expect_hash != $parts['hash']) { + if (!phutil_hashes_are_identical($expect_hash, $parts['hash'])) { throw new PhabricatorMetaMTAReceivedMailProcessingException( MetaMTAReceivedMailStatus::STATUS_HASH_MISMATCH, pht( diff --git a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php index 18d827f4c0..743082f47f 100644 --- a/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php +++ b/src/applications/metamta/view/PhabricatorMetaMTAMailBody.php @@ -43,16 +43,16 @@ final class PhabricatorMetaMTAMailBody extends Phobject { return $this; } - public function addRemarkupSection($text) { + public function addRemarkupSection($header, $text) { try { $engine = PhabricatorMarkupEngine::newMarkupEngine(array()); $engine->setConfig('viewer', $this->getViewer()); $engine->setMode(PhutilRemarkupEngine::MODE_TEXT); $styled_text = $engine->markupText($text); - $this->sections[] = $styled_text; + $this->addPlaintextSection($header, $styled_text); } catch (Exception $ex) { phlog($ex); - $this->sections[] = $text; + $this->addTextSection($header, $text); } try { @@ -63,14 +63,10 @@ final class PhabricatorMetaMTAMailBody extends Phobject { 'uri.base', PhabricatorEnv::getProductionURI('/')); $html = $mail_engine->markupText($text); - $this->htmlSections[] = $html; + $this->addHTMLSection($header, $html); } catch (Exception $ex) { phlog($ex); - $this->htmlSections[] = phutil_escape_html_newlines( - phutil_tag( - 'div', - array(), - $text)); + $this->addHTMLSection($header, $text); } return $this; @@ -121,12 +117,16 @@ final class PhabricatorMetaMTAMailBody extends Phobject { } public function addHTMLSection($header, $html_fragment) { + if ($header !== null) { + $header = phutil_tag('strong', array(), $header); + } + $this->htmlSections[] = array( phutil_tag( 'div', array(), array( - phutil_tag('strong', array(), $header), + $header, phutil_tag('div', array(), $html_fragment), )), ); @@ -212,5 +212,4 @@ final class PhabricatorMetaMTAMailBody extends Phobject { private function indent($text) { return rtrim(" ".str_replace("\n", "\n ", $text)); } - } diff --git a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php index 8feeeb1408..cb989f77cb 100644 --- a/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php +++ b/src/applications/owners/editor/PhabricatorOwnersPackageTransactionEditor.php @@ -205,18 +205,6 @@ final class PhabricatorOwnersPackageTransactionEditor return $errors; } - protected function extractFilePHIDsFromCustomTransaction( - PhabricatorLiskDAO $object, - PhabricatorApplicationTransaction $xaction) { - - switch ($xaction->getTransactionType()) { - case PhabricatorOwnersPackageTransaction::TYPE_DESCRIPTION: - return array($xaction->getNewValue()); - } - - return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); - } - protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { diff --git a/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php b/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php index edb4f5db8f..93ef9d419d 100644 --- a/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php +++ b/src/applications/owners/storage/PhabricatorOwnersPackageTransaction.php @@ -208,4 +208,16 @@ final class PhabricatorOwnersPackageTransaction return parent::renderChangeDetails($viewer); } + public function getRemarkupBlocks() { + $blocks = parent::getRemarkupBlocks(); + + switch ($this->getTransactionType()) { + case self::TYPE_DESCRIPTION: + $blocks[] = $this->getNewValue(); + break; + } + + return $blocks; + } + } diff --git a/src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php b/src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php new file mode 100644 index 0000000000..2aeca83c0a --- /dev/null +++ b/src/applications/owners/storage/PhabricatorOwnersSchemaSpec.php @@ -0,0 +1,10 @@ +buildEdgeSchemata(new PhabricatorOwnersPackage()); + } + +} diff --git a/src/applications/paste/conduit/PasteEditConduitAPIMethod.php b/src/applications/paste/conduit/PasteEditConduitAPIMethod.php index c02fb940e8..cfbb8de612 100644 --- a/src/applications/paste/conduit/PasteEditConduitAPIMethod.php +++ b/src/applications/paste/conduit/PasteEditConduitAPIMethod.php @@ -1,7 +1,7 @@ getViewer()); @@ -24,7 +34,7 @@ final class PhabricatorPasteEditEngine return $object->getMonogram(); } - protected function getObjectCreateShortText($object) { + protected function getObjectCreateShortText() { return pht('Create Paste'); } diff --git a/src/applications/policy/rule/PhabricatorAdministratorsPolicyRule.php b/src/applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php similarity index 100% rename from src/applications/policy/rule/PhabricatorAdministratorsPolicyRule.php rename to src/applications/people/policyrule/PhabricatorAdministratorsPolicyRule.php diff --git a/src/applications/policy/rule/PhabricatorUsersPolicyRule.php b/src/applications/people/policyrule/PhabricatorUsersPolicyRule.php similarity index 100% rename from src/applications/policy/rule/PhabricatorUsersPolicyRule.php rename to src/applications/people/policyrule/PhabricatorUsersPolicyRule.php diff --git a/src/applications/phame/application/PhabricatorPhameApplication.php b/src/applications/phame/application/PhabricatorPhameApplication.php index ec98087adf..1a3d3f90f4 100644 --- a/src/applications/phame/application/PhabricatorPhameApplication.php +++ b/src/applications/phame/application/PhabricatorPhameApplication.php @@ -54,6 +54,7 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { 'framed/(?P\d+)/' => 'PhamePostFramedController', 'new/' => 'PhamePostNewController', 'move/(?P\d+)/' => 'PhamePostNewController', + 'comment/(?P[1-9]\d*)/' => 'PhamePostCommentController', ), 'blog/' => array( '(?:(?Puser|all)/)?' => 'PhameBlogListController', @@ -102,4 +103,13 @@ final class PhabricatorPhameApplication extends PhabricatorApplication { ); } + protected function getCustomCapabilities() { + return array( + PhameBlogCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + 'caption' => pht('Default create policy for blogs.'), + ), + ); + } + } diff --git a/src/applications/phame/capability/PhameBlogCreateCapability.php b/src/applications/phame/capability/PhameBlogCreateCapability.php new file mode 100644 index 0000000000..e86f2327ab --- /dev/null +++ b/src/applications/phame/capability/PhameBlogCreateCapability.php @@ -0,0 +1,16 @@ +withPHIDs(array($blog_phid)) ->requireCapabilities( array( - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); @@ -85,7 +86,7 @@ final class PhameCreatePostConduitAPIMethod extends PhameConduitAPIMethod { $is_draft = $request->getValue('isDraft', false); if (!$is_draft) { $post->setDatePublished(time()); - $post->setVisibility(PhamePost::VISIBILITY_PUBLISHED); + $post->setVisibility(PhameConstants::VISIBILITY_PUBLISHED); } $post->setTitle($title); $phame_title = $request->getValue( diff --git a/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php b/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php index 06c20c7720..0ecb4d22eb 100644 --- a/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php +++ b/src/applications/phame/conduit/PhameQueryPostsConduitAPIMethod.php @@ -65,9 +65,9 @@ final class PhameQueryPostsConduitAPIMethod extends PhameConduitAPIMethod { $published = $request->getValue('published', null); if ($published === true) { - $query->withVisibility(PhamePost::VISIBILITY_PUBLISHED); + $query->withVisibility(PhameConstants::VISIBILITY_PUBLISHED); } else if ($published === false) { - $query->withVisibility(PhamePost::VISIBILITY_DRAFT); + $query->withVisibility(PhameConstants::VISIBILITY_DRAFT); } $published_after = $request->getValue('publishedAfter', null); diff --git a/src/applications/phame/constants/PhameConstants.php b/src/applications/phame/constants/PhameConstants.php new file mode 100644 index 0000000000..c9444411f0 --- /dev/null +++ b/src/applications/phame/constants/PhameConstants.php @@ -0,0 +1,23 @@ + pht('Published'), + self::VISIBILITY_DRAFT => pht('Draft'), + ); + } + + public static function getPhamePostStatusName($status) { + $map = array( + self::VISIBILITY_PUBLISHED => pht('Published'), + self::VISIBILITY_DRAFT => pht('Draft'), + ); + return idx($map, $status, pht('Unknown')); + } + +} diff --git a/src/applications/phame/controller/PhameController.php b/src/applications/phame/controller/PhameController.php index 0cfe877898..6606f7d30c 100644 --- a/src/applications/phame/controller/PhameController.php +++ b/src/applications/phame/controller/PhameController.php @@ -92,15 +92,22 @@ abstract class PhameController extends PhabricatorController { protected function buildApplicationCrumbs() { $crumbs = parent::buildApplicationCrumbs(); + + $can_create = $this->hasApplicationCapability( + PhameBlogCreateCapability::CAPABILITY); + $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('New Blog')) - ->setHref($this->getApplicationURI('/blog/new')) - ->setIcon('fa-plus-square')); + ->setHref($this->getApplicationURI('/blog/new/')) + ->setIcon('fa-plus-square') + ->setDisabled(!$can_create) + ->setWorkflow(!$can_create)); + $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('New Post')) - ->setHref($this->getApplicationURI('/post/new')) + ->setHref($this->getApplicationURI('/post/new/')) ->setIcon('fa-pencil')); return $crumbs; } diff --git a/src/applications/phame/controller/blog/PhameBlogEditController.php b/src/applications/phame/controller/blog/PhameBlogEditController.php index 24c5681583..0c2bb324dc 100644 --- a/src/applications/phame/controller/blog/PhameBlogEditController.php +++ b/src/applications/phame/controller/blog/PhameBlogEditController.php @@ -28,36 +28,41 @@ final class PhameBlogEditController $blog->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $v_projects = array_reverse($v_projects); + $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID( + $blog->getPHID()); } else { + $this->requireApplicationCapability( + PhameBlogCreateCapability::CAPABILITY); + $blog = PhameBlog::initializeNewBlog($viewer); $submit_button = pht('Create Blog'); $page_title = pht('Create Blog'); $cancel_uri = $this->getApplicationURI(); $v_projects = array(); + $v_cc = array(); } - $name = $blog->getName(); - $description = $blog->getDescription(); + $name = $blog->getName(); + $description = $blog->getDescription(); $custom_domain = $blog->getDomain(); - $skin = $blog->getSkin(); - $can_view = $blog->getViewPolicy(); - $can_edit = $blog->getEditPolicy(); - $can_join = $blog->getJoinPolicy(); + $skin = $blog->getSkin(); + $can_view = $blog->getViewPolicy(); + $can_edit = $blog->getEditPolicy(); $e_name = true; $e_custom_domain = null; $e_view_policy = null; $validation_exception = null; if ($request->isFormPost()) { - $name = $request->getStr('name'); - $description = $request->getStr('description'); + $name = $request->getStr('name'); + $description = $request->getStr('description'); $custom_domain = nonempty($request->getStr('custom_domain'), null); - $skin = $request->getStr('skin'); - $can_view = $request->getStr('can_view'); - $can_edit = $request->getStr('can_edit'); - $can_join = $request->getStr('can_join'); - $v_projects = $request->getArr('projects'); + $skin = $request->getStr('skin'); + $can_view = $request->getStr('can_view'); + $can_edit = $request->getStr('can_edit'); + $v_projects = $request->getArr('projects'); + $v_cc = $request->getArr('cc'); $xactions = array( id(new PhameBlogTransaction()) @@ -79,8 +84,8 @@ final class PhameBlogEditController ->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY) ->setNewValue($can_edit), id(new PhameBlogTransaction()) - ->setTransactionType(PhabricatorTransactions::TYPE_JOIN_POLICY) - ->setNewValue($can_join), + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) + ->setNewValue(array('=' => $v_cc)), ); $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; @@ -136,6 +141,13 @@ final class PhameBlogEditController ->setID('blog-description') ->setUser($viewer) ->setDisableMacros(true)) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Subscribers')) + ->setName('cc') + ->setValue($v_cc) + ->setUser($viewer) + ->setDatasource(new PhabricatorMetaMTAMailableDatasource())) ->appendChild( id(new AphrontFormPolicyControl()) ->setUser($viewer) @@ -153,14 +165,6 @@ final class PhameBlogEditController ->setPolicies($policies) ->setValue($can_edit) ->setName('can_edit')) - ->appendChild( - id(new AphrontFormPolicyControl()) - ->setUser($viewer) - ->setCapability(PhabricatorPolicyCapability::CAN_JOIN) - ->setPolicyObject($blog) - ->setPolicies($policies) - ->setValue($can_join) - ->setName('can_join')) ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Projects')) diff --git a/src/applications/phame/controller/blog/PhameBlogFeedController.php b/src/applications/phame/controller/blog/PhameBlogFeedController.php index 0941555ed2..b74465ef5e 100644 --- a/src/applications/phame/controller/blog/PhameBlogFeedController.php +++ b/src/applications/phame/controller/blog/PhameBlogFeedController.php @@ -21,7 +21,7 @@ final class PhameBlogFeedController extends PhameBlogController { $posts = id(new PhamePostQuery()) ->setViewer($viewer) ->withBlogPHIDs(array($blog->getPHID())) - ->withVisibility(PhamePost::VISIBILITY_PUBLISHED) + ->withVisibility(PhameConstants::VISIBILITY_PUBLISHED) ->execute(); $blog_uri = PhabricatorEnv::getProductionURI( diff --git a/src/applications/phame/controller/blog/PhameBlogViewController.php b/src/applications/phame/controller/blog/PhameBlogViewController.php index 03dfc8c1c6..2f20702f6e 100644 --- a/src/applications/phame/controller/blog/PhameBlogViewController.php +++ b/src/applications/phame/controller/blog/PhameBlogViewController.php @@ -39,8 +39,11 @@ final class PhameBlogViewController extends PhameBlogController { ->appendChild($post_list); $crumbs = $this->buildApplicationCrumbs(); - $crumbs->addTextCrumb(pht('Blogs'), $this->getApplicationURI('blog/')); - $crumbs->addTextCrumb($blog->getName(), $this->getApplicationURI()); + $crumbs->addTextCrumb( + pht('Blogs'), + $this->getApplicationURI('blog/')); + $crumbs->addTextCrumb( + $blog->getName()); $object_box = id(new PHUIObjectBoxView()) ->setHeader($header) @@ -100,10 +103,6 @@ final class PhameBlogViewController extends PhameBlogController { pht('Editable By'), $descriptions[PhabricatorPolicyCapability::CAN_EDIT]); - $properties->addProperty( - pht('Joinable By'), - $descriptions[PhabricatorPolicyCapability::CAN_JOIN]); - $engine = id(new PhabricatorMarkupEngine()) ->setViewer($viewer) ->addObject($blog, PhameBlog::MARKUP_FIELD_DESCRIPTION) @@ -136,18 +135,13 @@ final class PhameBlogViewController extends PhameBlogController { $blog, PhabricatorPolicyCapability::CAN_EDIT); - $can_join = PhabricatorPolicyFilter::hasCapability( - $viewer, - $blog, - PhabricatorPolicyCapability::CAN_JOIN); - $actions->addAction( id(new PhabricatorActionView()) ->setIcon('fa-plus') ->setHref($this->getApplicationURI('post/edit/?blog='.$blog->getID())) ->setName(pht('Write Post')) - ->setDisabled(!$can_join) - ->setWorkflow(!$can_join)); + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit)); $actions->addAction( id(new PhabricatorActionView()) diff --git a/src/applications/phame/controller/post/PhamePostCommentController.php b/src/applications/phame/controller/post/PhamePostCommentController.php new file mode 100644 index 0000000000..54c041ef94 --- /dev/null +++ b/src/applications/phame/controller/post/PhamePostCommentController.php @@ -0,0 +1,63 @@ +getViewer(); + $id = $request->getURIData('id'); + + if (!$request->isFormPost()) { + return new Aphront400Response(); + } + + $post = id(new PhamePostQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$post) { + return new Aphront404Response(); + } + + $is_preview = $request->isPreviewRequest(); + $draft = PhabricatorDraft::buildFromRequest($request); + + $view_uri = $this->getApplicationURI('post/view/'.$post->getID().'/'); + + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->attachComment( + id(new PhamePostTransactionComment()) + ->setContent($request->getStr('comment'))); + + $editor = id(new PhamePostEditor()) + ->setActor($viewer) + ->setContinueOnNoEffect($request->isContinueRequest()) + ->setContentSourceFromRequest($request) + ->setIsPreview($is_preview); + + try { + $xactions = $editor->applyTransactions($post, $xactions); + } catch (PhabricatorApplicationTransactionNoEffectException $ex) { + return id(new PhabricatorApplicationTransactionNoEffectResponse()) + ->setCancelURI($view_uri) + ->setException($ex); + } + + if ($draft) { + $draft->replaceOrDelete(); + } + + if ($request->isAjax() && $is_preview) { + return id(new PhabricatorApplicationTransactionResponse()) + ->setViewer($viewer) + ->setTransactions($xactions) + ->setIsPreview($is_preview); + } else { + return id(new AphrontRedirectResponse()) + ->setURI($view_uri); + } + } + +} diff --git a/src/applications/phame/controller/post/PhamePostEditController.php b/src/applications/phame/controller/post/PhamePostEditController.php index 3de92906a8..c768e3c3a0 100644 --- a/src/applications/phame/controller/post/PhamePostEditController.php +++ b/src/applications/phame/controller/post/PhamePostEditController.php @@ -27,6 +27,8 @@ final class PhamePostEditController extends PhamePostController { $post->getPHID(), PhabricatorProjectObjectHasProjectEdgeType::EDGECONST); $v_projects = array_reverse($v_projects); + $v_cc = PhabricatorSubscribersQuery::loadSubscribersForPHID( + $post->getPHID()); } else { $blog = id(new PhameBlogQuery()) ->setViewer($viewer) @@ -34,36 +36,38 @@ final class PhamePostEditController extends PhamePostController { ->requireCapabilities( array( PhabricatorPolicyCapability::CAN_VIEW, - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); if (!$blog) { return new Aphront404Response(); } $v_projects = array(); + $v_cc = array(); $post = PhamePost::initializePost($viewer, $blog); $cancel_uri = $this->getApplicationURI('/blog/view/'.$blog->getID().'/'); - $submit_button = pht('Save Draft'); - $page_title = pht('Create Post'); + $submit_button = pht('Create Post'); + $page_title = pht('Create Post'); } - $title = $post->getTitle(); - $phame_title = $post->getPhameTitle(); - $body = $post->getBody(); - $comments_widget = $post->getCommentsWidget(); + $title = $post->getTitle(); + $phame_title = $post->getPhameTitle(); + $body = $post->getBody(); + $visibility = $post->getVisibility(); $e_title = true; $e_phame_title = true; $validation_exception = null; if ($request->isFormPost()) { - $title = $request->getStr('title'); - $phame_title = $request->getStr('phame_title'); - $phame_title = PhabricatorSlug::normalize($phame_title); - $body = $request->getStr('body'); - $comments_widget = $request->getStr('comments_widget'); - $v_projects = $request->getArr('projects'); + $title = $request->getStr('title'); + $phame_title = $request->getStr('phame_title'); + $phame_title = PhabricatorSlug::normalize($phame_title); + $body = $request->getStr('body'); + $v_projects = $request->getArr('projects'); + $v_cc = $request->getArr('cc'); + $visibility = $request->getInt('visibility'); $xactions = array( id(new PhamePostTransaction()) @@ -76,8 +80,12 @@ final class PhamePostEditController extends PhamePostController { ->setTransactionType(PhamePostTransaction::TYPE_BODY) ->setNewValue($body), id(new PhamePostTransaction()) - ->setTransactionType(PhamePostTransaction::TYPE_COMMENTS_WIDGET) - ->setNewValue($comments_widget), + ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) + ->setNewValue($visibility), + id(new PhamePostTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_SUBSCRIBERS) + ->setNewValue(array('=' => $v_cc)), + ); $proj_edge_type = PhabricatorProjectObjectHasProjectEdgeType::EDGECONST; @@ -134,6 +142,12 @@ final class PhamePostEditController extends PhamePostController { 'with underscores for spaces. '. 'Formatting is enforced.')) ->setError($e_phame_title)) + ->appendChild( + id(new AphrontFormSelectControl()) + ->setLabel(pht('Visibility')) + ->setName('visibility') + ->setvalue($visibility) + ->setOptions(PhameConstants::getPhamePostStatusMap())) ->appendChild( id(new PhabricatorRemarkupControl()) ->setLabel(pht('Body')) @@ -143,18 +157,19 @@ final class PhamePostEditController extends PhamePostController { ->setID('post-body') ->setUser($viewer) ->setDisableMacros(true)) + ->appendControl( + id(new AphrontFormTokenizerControl()) + ->setLabel(pht('Subscribers')) + ->setName('cc') + ->setValue($v_cc) + ->setUser($viewer) + ->setDatasource(new PhabricatorMetaMTAMailableDatasource())) ->appendControl( id(new AphrontFormTokenizerControl()) ->setLabel(pht('Projects')) ->setName('projects') ->setValue($v_projects) ->setDatasource(new PhabricatorProjectDatasource())) - ->appendChild( - id(new AphrontFormSelectControl()) - ->setLabel(pht('Comments Widget')) - ->setName('comments_widget') - ->setvalue($comments_widget) - ->setOptions($post->getCommentsWidgetOptionsForSelect())) ->appendChild( id(new AphrontFormSubmitControl()) ->addCancelButton($cancel_uri) diff --git a/src/applications/phame/controller/post/PhamePostNewController.php b/src/applications/phame/controller/post/PhamePostNewController.php index 7098401749..8dd2e0d3c7 100644 --- a/src/applications/phame/controller/post/PhamePostNewController.php +++ b/src/applications/phame/controller/post/PhamePostNewController.php @@ -30,7 +30,7 @@ final class PhamePostNewController extends PhamePostController { ->withIDs(array($request->getInt('blog'))) ->requireCapabilities( array( - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_EDIT, )) ->executeOne(); @@ -52,7 +52,7 @@ final class PhamePostNewController extends PhamePostController { ->setViewer($viewer) ->requireCapabilities( array( - PhabricatorPolicyCapability::CAN_JOIN, + PhabricatorPolicyCapability::CAN_EDIT, )) ->execute(); @@ -65,7 +65,7 @@ final class PhamePostNewController extends PhamePostController { $notification = id(new PHUIInfoView()) ->setSeverity(PHUIInfoView::SEVERITY_NODATA) ->appendChild( - pht('You do not have permission to join any blogs. Create a blog '. + pht('You do not have permission to post to any blogs. Create a blog '. 'first, then you can post to it.')); } else { diff --git a/src/applications/phame/controller/post/PhamePostPublishController.php b/src/applications/phame/controller/post/PhamePostPublishController.php index 6ca8ff5d54..9b593b1313 100644 --- a/src/applications/phame/controller/post/PhamePostPublishController.php +++ b/src/applications/phame/controller/post/PhamePostPublishController.php @@ -21,9 +21,17 @@ final class PhamePostPublishController extends PhamePostController { $view_uri = $this->getApplicationURI('/post/view/'.$post->getID().'/'); if ($request->isFormPost()) { - $post->setVisibility(PhamePost::VISIBILITY_PUBLISHED); - $post->setDatePublished(time()); - $post->save(); + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) + ->setNewValue(PhameConstants::VISIBILITY_PUBLISHED); + + id(new PhamePostEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($post, $xactions); return id(new AphrontRedirectResponse())->setURI($view_uri); } diff --git a/src/applications/phame/controller/post/PhamePostUnpublishController.php b/src/applications/phame/controller/post/PhamePostUnpublishController.php index 80a320344d..ca09ef8879 100644 --- a/src/applications/phame/controller/post/PhamePostUnpublishController.php +++ b/src/applications/phame/controller/post/PhamePostUnpublishController.php @@ -19,9 +19,17 @@ final class PhamePostUnpublishController extends PhamePostController { } if ($request->isFormPost()) { - $post->setVisibility(PhamePost::VISIBILITY_DRAFT); - $post->setDatePublished(0); - $post->save(); + $xactions = array(); + $xactions[] = id(new PhamePostTransaction()) + ->setTransactionType(PhamePostTransaction::TYPE_VISIBILITY) + ->setNewValue(PhameConstants::VISIBILITY_DRAFT); + + id(new PhamePostEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true) + ->setContinueOnMissingFields(true) + ->applyTransactions($post, $xactions); return id(new AphrontRedirectResponse()) ->setURI($this->getApplicationURI('/post/view/'.$post->getID().'/')); diff --git a/src/applications/phame/controller/post/PhamePostViewController.php b/src/applications/phame/controller/post/PhamePostViewController.php index 3afdf5401d..6b414a9fe7 100644 --- a/src/applications/phame/controller/post/PhamePostViewController.php +++ b/src/applications/phame/controller/post/PhamePostViewController.php @@ -14,7 +14,18 @@ final class PhamePostViewController extends PhamePostController { return new Aphront404Response(); } + $blog = $post->getBlog(); + $crumbs = $this->buildApplicationCrumbs(); + if ($blog) { + $crumbs->addTextCrumb( + $blog->getName(), + $this->getApplicationURI('blog/view/'.$blog->getID().'/')); + } else { + $crumbs->addTextCrumb( + pht('[No Blog]'), + null); + } $crumbs->addTextCrumb( $post->getTitle(), $this->getApplicationURI('post/view/'.$post->getID().'/')); @@ -76,13 +87,24 @@ final class PhamePostViewController extends PhamePostController { ), $engine->getOutput($post, PhamePost::MARKUP_FIELD_BODY))); + $timeline = $this->buildTransactionTimeline( + $post, + id(new PhamePostTransactionQuery()) + ->withTransactionTypes(array(PhabricatorTransactions::TYPE_COMMENT))); + $timeline = phutil_tag_div('phui-document-view-pro-box', $timeline); + + $add_comment = $this->buildCommentForm($post); + return $this->newPage() ->setTitle($post->getTitle()) ->addClass('pro-white-background') + ->setPageObjectPHIDs(array($post->getPHID())) ->setCrumbs($crumbs) ->appendChild( array( $document, + $timeline, + $add_comment, )); } @@ -123,6 +145,7 @@ final class PhamePostViewController extends PhamePostController { id(new PhabricatorActionView()) ->setIcon('fa-eye') ->setHref($this->getApplicationURI('post/publish/'.$id.'/')) + ->setDisabled(!$can_edit) ->setName(pht('Preview / Publish'))); } else { $actions->addAction( @@ -130,6 +153,7 @@ final class PhamePostViewController extends PhamePostController { ->setIcon('fa-eye-slash') ->setHref($this->getApplicationURI('post/unpublish/'.$id.'/')) ->setName(pht('Unpublish')) + ->setDisabled(!$can_edit) ->setWorkflow(true)); } @@ -190,4 +214,27 @@ final class PhamePostViewController extends PhamePostController { return $properties; } + private function buildCommentForm(PhamePost $post) { + $viewer = $this->getViewer(); + + $is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business'); + + $add_comment_header = $is_serious + ? pht('Add Comment') + : pht('Derp Text'); + + $draft = PhabricatorDraft::newFromUserAndKey( + $viewer, $post->getPHID()); + + $box = id(new PhabricatorApplicationTransactionCommentView()) + ->setUser($viewer) + ->setObjectPHID($post->getPHID()) + ->setDraft($draft) + ->setHeaderText($add_comment_header) + ->setAction($this->getApplicationURI('post/comment/'.$post->getID().'/')) + ->setSubmitButtonName(pht('Add Comment')); + + return phutil_tag_div('phui-document-view-pro-box', $box); + } + } diff --git a/src/applications/phame/editor/PhameBlogEditor.php b/src/applications/phame/editor/PhameBlogEditor.php index d665d92541..712c4ddd5b 100644 --- a/src/applications/phame/editor/PhameBlogEditor.php +++ b/src/applications/phame/editor/PhameBlogEditor.php @@ -20,7 +20,6 @@ final class PhameBlogEditor $types[] = PhameBlogTransaction::TYPE_SKIN; $types[] = PhabricatorTransactions::TYPE_VIEW_POLICY; $types[] = PhabricatorTransactions::TYPE_EDIT_POLICY; - $types[] = PhabricatorTransactions::TYPE_JOIN_POLICY; return $types; } @@ -94,6 +93,7 @@ final class PhameBlogEditor $errors = parent::validateTransaction($object, $type, $xactions); + switch ($type) { case PhameBlogTransaction::TYPE_NAME: $missing = $this->validateIsEmptyTextField( @@ -112,6 +112,9 @@ final class PhameBlogEditor } break; case PhameBlogTransaction::TYPE_DOMAIN: + if (!$xactions) { + continue; + } $custom_domain = last($xactions)->getNewValue(); if (empty($custom_domain)) { continue; @@ -158,15 +161,66 @@ final class PhameBlogEditor protected function shouldSendMail( PhabricatorLiskDAO $object, array $xactions) { - return false; + return true; } protected function shouldPublishFeedStory( PhabricatorLiskDAO $object, array $xactions) { - return false; + return true; } + protected function getMailTo(PhabricatorLiskDAO $object) { + $phids = array(); + $phids[] = $this->requireActor()->getPHID(); + $phids[] = $object->getCreatorPHID(); + + return $phids; + } + + protected function buildMailTemplate(PhabricatorLiskDAO $object) { + $phid = $object->getPHID(); + $name = $object->getName(); + + return id(new PhabricatorMetaMTAMail()) + ->setSubject($name) + ->addHeader('Thread-Topic', $phid); + } + + protected function buildReplyHandler(PhabricatorLiskDAO $object) { + return id(new PhameBlogReplyHandler()) + ->setMailReceiver($object); + } + + protected function buildMailBody( + PhabricatorLiskDAO $object, + array $xactions) { + + $body = parent::buildMailBody($object, $xactions); + + $body->addLinkSection( + pht('BLOG DETAIL'), + PhabricatorEnv::getProductionURI($object->getViewURI())); + + return $body; + } + + public function getMailTagsMap() { + return array( + PhameBlogTransaction::MAILTAG_DETAILS => + pht("A blog's details change."), + PhameBlogTransaction::MAILTAG_SUBSCRIBERS => + pht("A blog's subscribers change."), + PhameBlogTransaction::MAILTAG_OTHER => + pht('Other blog activity not listed above occurs.'), + ); + } + + protected function getMailSubjectPrefix() { + return '[Phame]'; + } + + protected function supportsSearch() { return false; } diff --git a/src/applications/phame/editor/PhamePostEditor.php b/src/applications/phame/editor/PhamePostEditor.php index ab4b1a1465..330ce86c19 100644 --- a/src/applications/phame/editor/PhamePostEditor.php +++ b/src/applications/phame/editor/PhamePostEditor.php @@ -17,7 +17,8 @@ final class PhamePostEditor $types[] = PhamePostTransaction::TYPE_TITLE; $types[] = PhamePostTransaction::TYPE_PHAME_TITLE; $types[] = PhamePostTransaction::TYPE_BODY; - $types[] = PhamePostTransaction::TYPE_COMMENTS_WIDGET; + $types[] = PhamePostTransaction::TYPE_VISIBILITY; + $types[] = PhabricatorTransactions::TYPE_COMMENT; return $types; } @@ -33,8 +34,8 @@ final class PhamePostEditor return $object->getPhameTitle(); case PhamePostTransaction::TYPE_BODY: return $object->getBody(); - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: - return $object->getCommentsWidget(); + case PhamePostTransaction::TYPE_VISIBILITY: + return $object->getVisibility(); } } @@ -46,7 +47,7 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_TITLE: case PhamePostTransaction::TYPE_PHAME_TITLE: case PhamePostTransaction::TYPE_BODY: - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: + case PhamePostTransaction::TYPE_VISIBILITY: return $xaction->getNewValue(); } } @@ -62,8 +63,13 @@ final class PhamePostEditor return $object->setPhameTitle($xaction->getNewValue()); case PhamePostTransaction::TYPE_BODY: return $object->setBody($xaction->getNewValue()); - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: - return $object->setCommentsWidget($xaction->getNewValue()); + case PhamePostTransaction::TYPE_VISIBILITY: + if ($xaction->getNewValue() == PhameConstants::VISIBILITY_DRAFT) { + $object->setDatePublished(0); + } else { + $object->setDatePublished(time()); + } + return $object->setVisibility($xaction->getNewValue()); } return parent::applyCustomInternalTransaction($object, $xaction); @@ -77,7 +83,7 @@ final class PhamePostEditor case PhamePostTransaction::TYPE_TITLE: case PhamePostTransaction::TYPE_PHAME_TITLE: case PhamePostTransaction::TYPE_BODY: - case PhamePostTransaction::TYPE_COMMENTS_WIDGET: + case PhamePostTransaction::TYPE_VISIBILITY: return; } @@ -109,6 +115,9 @@ final class PhamePostEditor } break; case PhamePostTransaction::TYPE_PHAME_TITLE: + if (!$xactions) { + continue; + } $missing = $this->validateIsEmptyTextField( $object->getPhameTitle(), $xactions); @@ -171,8 +180,11 @@ final class PhamePostEditor $blog_phid = $object->getBlogPHID(); if ($blog_phid) { - $phids[] = PhabricatorSubscribersQuery::loadSubscribersForPHID( + $cc_phids = PhabricatorSubscribersQuery::loadSubscribersForPHID( $blog_phid); + foreach ($cc_phids as $cc) { + $phids[] = $cc; + } } return $phids; } @@ -197,6 +209,10 @@ final class PhamePostEditor $body = parent::buildMailBody($object, $xactions); + if ($this->getIsNewObject()) { + $body->addRemarkupSection(null, $object->getBody()); + } + $body->addLinkSection( pht('POST DETAIL'), PhabricatorEnv::getProductionURI($object->getViewURI())); diff --git a/src/applications/phame/mail/PhameBlogReplyHandler.php b/src/applications/phame/mail/PhameBlogReplyHandler.php new file mode 100644 index 0000000000..a6e245709e --- /dev/null +++ b/src/applications/phame/mail/PhameBlogReplyHandler.php @@ -0,0 +1,21 @@ +setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + } + + protected function getTransactionReplyHandler() { + return new PhamePostReplyHandler(); + } + +} diff --git a/src/applications/phame/mail/PhamePostReplyHandler.php b/src/applications/phame/mail/PhamePostReplyHandler.php index f994763709..5e47edf43c 100644 --- a/src/applications/phame/mail/PhamePostReplyHandler.php +++ b/src/applications/phame/mail/PhamePostReplyHandler.php @@ -14,8 +14,4 @@ final class PhamePostReplyHandler return PhabricatorPhamePostPHIDType::TYPECONST; } - protected function shouldCreateCommentFromMailBody() { - return false; - } - } diff --git a/src/applications/phame/query/PhameBlogTransactionQuery.php b/src/applications/phame/query/PhameBlogTransactionQuery.php new file mode 100644 index 0000000000..77a056b682 --- /dev/null +++ b/src/applications/phame/query/PhameBlogTransactionQuery.php @@ -0,0 +1,10 @@ +setLabel(pht('Visibility')) ->setOptions(array( '' => pht('All'), - PhamePost::VISIBILITY_PUBLISHED => pht('Live'), - PhamePost::VISIBILITY_DRAFT => pht('Draft'), + PhameConstants::VISIBILITY_PUBLISHED => pht('Published'), + PhameConstants::VISIBILITY_DRAFT => pht('Draft'), )), ); } @@ -45,7 +45,7 @@ final class PhamePostSearchEngine protected function getBuiltinQueryNames() { $names = array( 'all' => pht('All Posts'), - 'live' => pht('Live Posts'), + 'live' => pht('Published Posts'), 'draft' => pht('Draft Posts'), ); return $names; @@ -60,10 +60,10 @@ final class PhamePostSearchEngine return $query; case 'live': return $query->setParameter( - 'visibility', PhamePost::VISIBILITY_PUBLISHED); + 'visibility', PhameConstants::VISIBILITY_PUBLISHED); case 'draft': return $query->setParameter( - 'visibility', PhamePost::VISIBILITY_DRAFT); + 'visibility', PhameConstants::VISIBILITY_DRAFT); } return parent::buildSavedQueryFromBuiltin($query_key); @@ -81,15 +81,20 @@ final class PhamePostSearchEngine foreach ($posts as $post) { $id = $post->getID(); - $blog = $viewer->renderHandle($post->getBlogPHID())->render(); + $blog = $post->getBlog(); + if ($blog) { + $blog_name = $viewer->renderHandle($post->getBlogPHID())->render(); + $blog_name = pht('Blog: %s', $blog_name); + } else { + $blog_name = pht('[No Blog]'); + } $item = id(new PHUIObjectItemView()) ->setUser($viewer) ->setObject($post) ->setHeader($post->getTitle()) ->setStatusIcon('fa-star') ->setHref($this->getApplicationURI("/post/view/{$id}/")) - ->addAttribute( - pht('Blog: %s', $blog)); + ->addAttribute($blog_name); if ($post->isDraft()) { $item->setStatusIcon('fa-star-o grey'); $item->setDisabled(true); diff --git a/src/applications/phame/storage/PhameBlog.php b/src/applications/phame/storage/PhameBlog.php index 6fb8c409f6..3082592a93 100644 --- a/src/applications/phame/storage/PhameBlog.php +++ b/src/applications/phame/storage/PhameBlog.php @@ -20,7 +20,7 @@ final class PhameBlog extends PhameDAO protected $creatorPHID; protected $viewPolicy; protected $editPolicy; - protected $joinPolicy; + protected $mailKey; private static $requestBlog; @@ -34,10 +34,10 @@ final class PhameBlog extends PhameDAO 'name' => 'text64', 'description' => 'text', 'domain' => 'text128?', + 'mailKey' => 'bytes20', // T6203/NULLABILITY // These policies should always be non-null. - 'joinPolicy' => 'policy?', 'editPolicy' => 'policy?', 'viewPolicy' => 'policy?', ), @@ -55,6 +55,13 @@ final class PhameBlog extends PhameDAO ) + parent::getConfiguration(); } + public function save() { + if (!$this->getMailKey()) { + $this->setMailKey(Filesystem::readRandomCharacters(20)); + } + return parent::save(); + } + public function generatePHID() { return PhabricatorPHID::generateNewPHID( PhabricatorPhameBlogPHIDType::TYPECONST); @@ -64,8 +71,7 @@ final class PhameBlog extends PhameDAO $blog = id(new PhameBlog()) ->setCreatorPHID($actor->getPHID()) ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) - ->setEditPolicy(PhabricatorPolicies::POLICY_USER) - ->setJoinPolicy(PhabricatorPolicies::POLICY_USER); + ->setEditPolicy(PhabricatorPolicies::POLICY_USER); return $blog; } @@ -214,6 +220,11 @@ final class PhameBlog extends PhameDAO return $base; } + public function getViewURI() { + $uri = '/phame/blog/view/'.$this->getID().'/'; + return PhabricatorEnv::getProductionURI($uri); + } + /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ @@ -222,7 +233,6 @@ final class PhameBlog extends PhameDAO return array( PhabricatorPolicyCapability::CAN_VIEW, PhabricatorPolicyCapability::CAN_EDIT, - PhabricatorPolicyCapability::CAN_JOIN, ); } @@ -233,14 +243,11 @@ final class PhameBlog extends PhameDAO return $this->getViewPolicy(); case PhabricatorPolicyCapability::CAN_EDIT: return $this->getEditPolicy(); - case PhabricatorPolicyCapability::CAN_JOIN: - return $this->getJoinPolicy(); } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { $can_edit = PhabricatorPolicyCapability::CAN_EDIT; - $can_join = PhabricatorPolicyCapability::CAN_JOIN; switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: @@ -248,15 +255,6 @@ final class PhameBlog extends PhameDAO if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { return true; } - if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_join)) { - return true; - } - break; - case PhabricatorPolicyCapability::CAN_JOIN: - // Users who can edit a blog can always post to it. - if (PhabricatorPolicyFilter::hasCapability($user, $this, $can_edit)) { - return true; - } break; } @@ -268,10 +266,7 @@ final class PhameBlog extends PhameDAO switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: return pht( - 'Users who can edit or post on a blog can always view it.'); - case PhabricatorPolicyCapability::CAN_JOIN: - return pht( - 'Users who can edit a blog can always post on it.'); + 'Users who can edit a blog can always view it.'); } return null; diff --git a/src/applications/phame/storage/PhameBlogTransaction.php b/src/applications/phame/storage/PhameBlogTransaction.php index f1e558e240..0954e72a37 100644 --- a/src/applications/phame/storage/PhameBlogTransaction.php +++ b/src/applications/phame/storage/PhameBlogTransaction.php @@ -8,6 +8,10 @@ final class PhameBlogTransaction const TYPE_DOMAIN = 'phame.blog.domain'; const TYPE_SKIN = 'phame.blog.skin'; + const MAILTAG_DETAILS = 'phame-blog-details'; + const MAILTAG_SUBSCRIBERS = 'phame-blog-subscribers'; + const MAILTAG_OTHER = 'phame-blog-other'; + public function getApplicationName() { return 'phame'; } @@ -44,6 +48,26 @@ final class PhameBlogTransaction return parent::getIcon(); } + public function getMailTags() { + $tags = parent::getMailTags(); + + switch ($this->getTransactionType()) { + case PhabricatorTransactions::TYPE_SUBSCRIBERS: + $tags[] = self::MAILTAG_SUBSCRIBERS; + break; + case self::TYPE_NAME: + case self::TYPE_DESCRIPTION: + case self::TYPE_DOMAIN: + case self::TYPE_SKIN: + $tags[] = self::MAILTAG_DETAILS; + break; + default: + $tags[] = self::MAILTAG_OTHER; + break; + } + return $tags; + } + public function getTitle() { $author_phid = $this->getAuthorPHID(); $object_phid = $this->getObjectPHID(); @@ -53,7 +77,7 @@ final class PhameBlogTransaction $type = $this->getTransactionType(); switch ($type) { - case self:TYPE_NAME: + case self::TYPE_NAME: if ($old === null) { return pht( '%s created this blog.', diff --git a/src/applications/phame/storage/PhamePost.php b/src/applications/phame/storage/PhamePost.php index eecb27b566..eb04679e1f 100644 --- a/src/applications/phame/storage/PhamePost.php +++ b/src/applications/phame/storage/PhamePost.php @@ -13,9 +13,6 @@ final class PhamePost extends PhameDAO const MARKUP_FIELD_BODY = 'markup:body'; const MARKUP_FIELD_SUMMARY = 'markup:summary'; - const VISIBILITY_DRAFT = 0; - const VISIBILITY_PUBLISHED = 1; - protected $bloggerPHID; protected $title; protected $phameTitle; @@ -37,7 +34,7 @@ final class PhamePost extends PhameDAO ->setBlogPHID($blog->getPHID()) ->setBlog($blog) ->setDatePublished(0) - ->setVisibility(self::VISIBILITY_DRAFT); + ->setVisibility(PhameConstants::VISIBILITY_PUBLISHED); return $post; } @@ -66,7 +63,7 @@ final class PhamePost extends PhameDAO } public function isDraft() { - return $this->getVisibility() == self::VISIBILITY_DRAFT; + return $this->getVisibility() == PhameConstants::VISIBILITY_DRAFT; } public function getHumanName() { @@ -79,20 +76,6 @@ final class PhamePost extends PhameDAO return $name; } - public function setCommentsWidget($widget) { - $config_data = $this->getConfigData(); - $config_data['comments_widget'] = $widget; - return $this; - } - - public function getCommentsWidget() { - $config_data = $this->getConfigData(); - if (empty($config_data)) { - return 'none'; - } - return idx($config_data, 'comments_widget', 'none'); - } - protected function getConfiguration() { return array( self::CONFIG_AUX_PHID => true, @@ -165,31 +148,6 @@ final class PhamePost extends PhameDAO ); } - public static function getVisibilityOptionsForSelect() { - return array( - self::VISIBILITY_DRAFT => pht('Draft: visible only to me.'), - self::VISIBILITY_PUBLISHED => pht( - 'Published: visible to the whole world.'), - ); - } - - public function getCommentsWidgetOptionsForSelect() { - $current = $this->getCommentsWidget(); - $options = array(); - - if ($current == 'facebook' || - PhabricatorFacebookAuthProvider::getFacebookApplicationID()) { - $options['facebook'] = pht('Facebook'); - } - if ($current == 'disqus' || - PhabricatorEnv::getEnvConfig('disqus.shortname')) { - $options['disqus'] = pht('Disqus'); - } - $options['none'] = pht('None'); - - return $options; - } - /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ @@ -209,18 +167,23 @@ final class PhamePost extends PhameDAO case PhabricatorPolicyCapability::CAN_VIEW: if (!$this->isDraft() && $this->getBlog()) { return $this->getBlog()->getViewPolicy(); + } else if ($this->getBlog()) { + return $this->getBlog()->getEditPolicy(); } else { return PhabricatorPolicies::POLICY_NOONE; } break; case PhabricatorPolicyCapability::CAN_EDIT: - return PhabricatorPolicies::POLICY_NOONE; + if ($this->getBlog()) { + return $this->getBlog()->getEditPolicy(); + } else { + return PhabricatorPolicies::POLICY_NOONE; + } } } public function hasAutomaticCapability($capability, PhabricatorUser $user) { - // A blog post's author can always view it, and is the only user allowed - // to edit it. + // A blog post's author can always view it. switch ($capability) { case PhabricatorPolicyCapability::CAN_VIEW: diff --git a/src/applications/phame/storage/PhamePostTransaction.php b/src/applications/phame/storage/PhamePostTransaction.php index 38910c9845..c985d41c25 100644 --- a/src/applications/phame/storage/PhamePostTransaction.php +++ b/src/applications/phame/storage/PhamePostTransaction.php @@ -6,7 +6,7 @@ final class PhamePostTransaction const TYPE_TITLE = 'phame.post.title'; const TYPE_PHAME_TITLE = 'phame.post.phame.title'; const TYPE_BODY = 'phame.post.body'; - const TYPE_COMMENTS_WIDGET = 'phame.post.comments.widget'; + const TYPE_VISIBILITY = 'phame.post.visibility'; const MAILTAG_CONTENT = 'phame-post-content'; const MAILTAG_COMMENT = 'phame-post-comment'; @@ -20,6 +20,10 @@ final class PhamePostTransaction return PhabricatorPhamePostPHIDType::TYPECONST; } + public function getApplicationTransactionCommentObject() { + return new PhamePostTransactionComment(); + } + public function getRemarkupBlocks() { $blocks = parent::getRemarkupBlocks(); @@ -54,7 +58,7 @@ final class PhamePostTransaction break; case self::TYPE_PHAME_TITLE: case self::TYPE_BODY: - case self::TYPE_COMMENTS_WIDGET: + case self::TYPE_VISIBILITY: return 'fa-pencil'; break; } @@ -65,7 +69,6 @@ final class PhamePostTransaction $tags = parent::getMailTags(); switch ($this->getTransactionType()) { - case self::TYPE_COMMENTS_WIDGET: case PhabricatorTransactions::TYPE_COMMENT: $tags[] = self::MAILTAG_COMMENT; break; @@ -108,18 +111,23 @@ final class PhamePostTransaction '%s updated the blog post.', $this->renderHandleLink($author_phid)); break; + case self::TYPE_VISIBILITY: + if ($new == PhameConstants::VISIBILITY_DRAFT) { + return pht( + '%s marked this post as a draft.', + $this->renderHandleLink($author_phid)); + } else { + return pht( + '%s published this post.', + $this->renderHandleLink($author_phid)); + } + break; case self::TYPE_PHAME_TITLE: return pht( '%s updated the post\'s Phame title to "%s".', $this->renderHandleLink($author_phid), rtrim($new, '/')); break; - case self::TYPE_COMMENTS_WIDGET: - return pht( - '%s updated the post\'s comment widget to "%s".', - $this->renderHandleLink($author_phid), - $new); - break; } return parent::getTitle(); @@ -153,37 +161,57 @@ final class PhamePostTransaction $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; + case self::TYPE_VISIBILITY: + if ($new == PhameConstants::VISIBILITY_DRAFT) { + return pht( + '%s marked %s as a draft.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } else { + return pht( + '%s published %s.', + $this->renderHandleLink($author_phid), + $this->renderHandleLink($object_phid)); + } + break; case self::TYPE_PHAME_TITLE: return pht( '%s updated the Phame title for %s.', $this->renderHandleLink($author_phid), $this->renderHandleLink($object_phid)); break; - case self::TYPE_COMMENTS_WIDGET: - return pht( - '%s updated the comments widget for %s.', - $this->renderHandleLink($author_phid), - $this->renderHandleLink($object_phid)); - break; } return parent::getTitleForFeed(); } public function getBodyForFeed(PhabricatorFeedStory $story) { - $new = $this->getNewValue(); - - $body = null; - + $text = null; switch ($this->getTransactionType()) { case self::TYPE_TITLE: + if ($this->getOldValue() === null) { + $post = $story->getPrimaryObject(); + $text = $post->getBody(); + } + break; + case self::TYPE_VISIBILITY: + if ($this->getNewValue() == PhameConstants::VISIBILITY_PUBLISHED) { + $post = $story->getPrimaryObject(); + $text = $post->getBody(); + } + break; case self::TYPE_BODY: - return phutil_escape_html_newlines( - id(new PhutilUTF8StringTruncator()) - ->setMaximumGlyphs(128) - ->truncateString($new)); + $text = $this->getNewValue(); break; } + + if (strlen($text)) { + return phutil_escape_html_newlines( + id(new PhutilUTF8StringTruncator()) + ->setMaximumGlyphs(128) + ->truncateString($text)); + } + return parent::getBodyForFeed($story); } @@ -201,7 +229,6 @@ final class PhamePostTransaction return parent::getColor(); } - public function hasChangeDetails() { switch ($this->getTransactionType()) { case self::TYPE_BODY: diff --git a/src/applications/phame/storage/PhamePostTransactionComment.php b/src/applications/phame/storage/PhamePostTransactionComment.php new file mode 100644 index 0000000000..4a04495d0b --- /dev/null +++ b/src/applications/phame/storage/PhamePostTransactionComment.php @@ -0,0 +1,10 @@ +getSummary()); } - public function renderComments() { - $post = $this->getPost(); - - switch ($post->getCommentsWidget()) { - case 'facebook': - $comments = $this->renderFacebookComments(); - break; - case 'disqus': - $comments = $this->renderDisqusComments(); - break; - case 'none': - default: - $comments = null; - break; - } - return $comments; - } - public function render() { return phutil_tag( 'div', @@ -129,7 +111,6 @@ final class PhamePostView extends AphrontView { $this->renderTitle(), $this->renderDatePublished(), $this->renderBody(), - $this->renderComments(), )); } @@ -146,96 +127,4 @@ final class PhamePostView extends AphrontView { )); } - private function renderFacebookComments() { - $fb_id = PhabricatorFacebookAuthProvider::getFacebookApplicationID(); - if (!$fb_id) { - return null; - } - - $fb_root = phutil_tag('div', - array( - 'id' => 'fb-root', - ), - ''); - - $c_uri = '//connect.facebook.net/en_US/all.js#xfbml=1&appId='.$fb_id; - $fb_js = CelerityStaticResourceResponse::renderInlineScript( - jsprintf( - '(function(d, s, id) {'. - ' var js, fjs = d.getElementsByTagName(s)[0];'. - ' if (d.getElementById(id)) return;'. - ' js = d.createElement(s); js.id = id;'. - ' js.src = %s;'. - ' fjs.parentNode.insertBefore(js, fjs);'. - '}(document, \'script\', \'facebook-jssdk\'));', - $c_uri)); - - - $uri = $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()); - - require_celerity_resource('phame-css'); - $fb_comments = phutil_tag('div', - array( - 'class' => 'fb-comments', - 'data-href' => $uri, - 'data-num-posts' => 5, - ), - ''); - - return phutil_tag( - 'div', - array( - 'class' => 'phame-comments-facebook', - ), - array( - $fb_root, - $fb_js, - $fb_comments, - )); - } - - private function renderDisqusComments() { - $disqus_shortname = PhabricatorEnv::getEnvConfig('disqus.shortname'); - if (!$disqus_shortname) { - return null; - } - - $post = $this->getPost(); - - $disqus_thread = phutil_tag('div', - array( - 'id' => 'disqus_thread', - )); - - // protip - try some var disqus_developer = 1; action to test locally - $disqus_js = CelerityStaticResourceResponse::renderInlineScript( - jsprintf( - ' var disqus_shortname = %s;'. - ' var disqus_identifier = %s;'. - ' var disqus_url = %s;'. - ' var disqus_title = %s;'. - '(function() {'. - ' var dsq = document.createElement("script");'. - ' dsq.type = "text/javascript";'. - ' dsq.async = true;'. - ' dsq.src = "//" + disqus_shortname + ".disqus.com/embed.js";'. - '(document.getElementsByTagName("head")[0] ||'. - ' document.getElementsByTagName("body")[0]).appendChild(dsq);'. - '})();', - $disqus_shortname, - $post->getPHID(), - $this->getSkin()->getURI('post/'.$this->getPost()->getPhameTitle()), - $post->getTitle())); - - return phutil_tag( - 'div', - array( - 'class' => 'phame-comments-disqus', - ), - array( - $disqus_thread, - $disqus_js, - )); - } - } diff --git a/src/applications/pholio/editor/PholioMockEditor.php b/src/applications/pholio/editor/PholioMockEditor.php index 14d3a0af16..31d1602202 100644 --- a/src/applications/pholio/editor/PholioMockEditor.php +++ b/src/applications/pholio/editor/PholioMockEditor.php @@ -120,19 +120,31 @@ final class PholioMockEditor extends PhabricatorApplicationTransactionEditor { PhabricatorLiskDAO $object, PhabricatorApplicationTransaction $xaction) { + $images = $this->getNewImages(); + $images = mpull($images, null, 'getPHID'); + switch ($xaction->getTransactionType()) { case PholioTransaction::TYPE_IMAGE_FILE: - $new = $xaction->getNewValue(); - $phids = array(); - foreach ($new as $key => $images) { - $phids[] = mpull($images, 'getFilePHID'); + $file_phids = array(); + foreach ($xaction->getNewValue() as $image_phid) { + $image = idx($images, $image_phid); + if (!$image) { + continue; + } + $file_phids[] = $image->getFilePHID(); } - return array_mergev($phids); + return $file_phids; case PholioTransaction::TYPE_IMAGE_REPLACE: - return array($xaction->getNewValue()->getFilePHID()); + $image_phid = $xaction->getNewValue(); + $image = idx($images, $image_phid); + + if ($image) { + return array($image->getFilePHID()); + } + break; } - return array(); + return parent::extractFilePHIDsFromCustomTransaction($object, $xaction); } diff --git a/src/applications/phortune/editor/PhortuneCartEditor.php b/src/applications/phortune/editor/PhortuneCartEditor.php index 02f9b729de..5196e12429 100644 --- a/src/applications/phortune/editor/PhortuneCartEditor.php +++ b/src/applications/phortune/editor/PhortuneCartEditor.php @@ -145,7 +145,7 @@ final class PhortuneCartEditor "%s", $issues); - $body->addRemarkupSection($overview); + $body->addRemarkupSection(null, $overview); $body->addLinkSection( pht('PAY NOW'), diff --git a/src/applications/phortune/exception/PhortuneNotImplementedException.php b/src/applications/phortune/exception/PhortuneNotImplementedException.php deleted file mode 100644 index eac3b3b7a8..0000000000 --- a/src/applications/phortune/exception/PhortuneNotImplementedException.php +++ /dev/null @@ -1,12 +0,0 @@ -buildStandardPageView(); $page->setApplicationName('XHPASTView'); diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php index 2c5a43687c..89f5b8fd7d 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewFrameController.php @@ -14,7 +14,7 @@ final class PhabricatorXHPASTViewFrameController phutil_tag( 'iframe', array( - 'src' => '/xhpast/frameset/'.$id.'/', + 'src' => "/xhpast/frameset/{$id}/", 'frameborder' => '0', 'style' => 'width: 100%; height: 800px;', '', diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php index de446b5e44..6f186fb3f8 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewFramesetController.php @@ -10,17 +10,15 @@ final class PhabricatorXHPASTViewFramesetController public function handleRequest(AphrontRequest $request) { $id = $request->getURIData('id'); - $response = new AphrontWebpageResponse(); - $response->setFrameable(true); - $response->setContent(phutil_tag( - 'frameset', - array('cols' => '33%, 34%, 33%'), - array( - phutil_tag('frame', array('src' => "/xhpast/input/{$id}/")), - phutil_tag('frame', array('src' => "/xhpast/tree/{$id}/")), - phutil_tag('frame', array('src' => "/xhpast/stream/{$id}/")), - ))); - - return $response; + return id(new AphrontWebpageResponse()) + ->setFrameable(true) + ->setContent(phutil_tag( + 'frameset', + array('cols' => '33%, 34%, 33%'), + array( + phutil_tag('frame', array('src' => "/xhpast/input/{$id}/")), + phutil_tag('frame', array('src' => "/xhpast/tree/{$id}/")), + phutil_tag('frame', array('src' => "/xhpast/stream/{$id}/")), + ))); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php index 8f824dc03f..af6c1ee761 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewPanelController.php @@ -12,8 +12,9 @@ abstract class PhabricatorXHPASTViewPanelController public function willProcessRequest(array $data) { $this->id = $data['id']; - $this->storageTree = id(new PhabricatorXHPASTViewParseTree()) + $this->storageTree = id(new PhabricatorXHPASTParseTree()) ->load($this->id); + if (!$this->storageTree) { throw new Exception(pht('No such AST!')); } @@ -65,10 +66,9 @@ li span { '', $content); - $response = new AphrontWebpageResponse(); - $response->setFrameable(true); - $response->setContent($content); - return $response; + return id(new AphrontWebpageResponse()) + ->setFrameable(true) + ->setContent($content); } } diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php index dd9cf85433..290666e375 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewRunController.php @@ -13,17 +13,21 @@ final class PhabricatorXHPASTViewRunController $resolved = $future->resolve(); // This is just to let it throw exceptions if stuff is broken. - $parse_tree = XHPASTTree::newFromDataAndResolvedExecFuture( - $source, - $resolved); + try { + XHPASTTree::newFromDataAndResolvedExecFuture($source, $resolved); + } catch (XHPASTSyntaxErrorException $ex) { + // This is possibly expected. + } list($err, $stdout, $stderr) = $resolved; - $storage_tree = new PhabricatorXHPASTViewParseTree(); - $storage_tree->setInput($source); - $storage_tree->setStdout($stdout); - $storage_tree->setAuthorPHID($viewer->getPHID()); - $storage_tree->save(); + $storage_tree = id(new PhabricatorXHPASTParseTree()) + ->setInput($source) + ->setReturnCode($err) + ->setStdout($stdout) + ->setStderr($stderr) + ->setAuthorPHID($viewer->getPHID()) + ->save(); return id(new AphrontRedirectResponse()) ->setURI('/xhpast/view/'.$storage_tree->getID().'/'); diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php index 3fe1046f10..f5cedf225a 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewStreamController.php @@ -6,11 +6,17 @@ final class PhabricatorXHPASTViewStreamController public function handleRequest(AphrontRequest $request) { $storage = $this->getStorageTree(); $input = $storage->getInput(); + $err = $storage->getReturnCode(); $stdout = $storage->getStdout(); + $stderr = $storage->getStderr(); - $tree = XHPASTTree::newFromDataAndResolvedExecFuture( - $input, - array(0, $stdout, '')); + try { + $tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $input, + array($err, $stdout, $stderr)); + } catch (XHPASTSyntaxErrorException $ex) { + return $this->buildXHPASTViewPanelResponse($ex->getMessage()); + } $tokens = array(); foreach ($tree->getRawTokenStream() as $id => $token) { diff --git a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php index 1b4eec6441..c15bdc2928 100644 --- a/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php +++ b/src/applications/phpast/controller/PhabricatorXHPASTViewTreeController.php @@ -10,18 +10,23 @@ final class PhabricatorXHPASTViewTreeController public function handleRequest(AphrontRequest $request) { $storage = $this->getStorageTree(); $input = $storage->getInput(); + $err = $storage->getReturnCode(); $stdout = $storage->getStdout(); + $stderr = $storage->getStderr(); - $tree = XHPASTTree::newFromDataAndResolvedExecFuture( - $input, - array(0, $stdout, '')); + try { + $tree = XHPASTTree::newFromDataAndResolvedExecFuture( + $input, + array($err, $stdout, $stderr)); + } catch (XHPASTSyntaxErrorException $ex) { + return $this->buildXHPASTViewPanelResponse($ex->getMessage()); + } $tree = phutil_tag('ul', array(), $this->buildTree($tree->getRootNode())); return $this->buildXHPASTViewPanelResponse($tree); } protected function buildTree($root) { - try { $name = $root->getTypeName(); $title = $root->getDescription(); diff --git a/src/applications/phpast/storage/PhabricatorXHPASTDAO.php b/src/applications/phpast/storage/PhabricatorXHPASTDAO.php new file mode 100644 index 0000000000..e22b0ed94f --- /dev/null +++ b/src/applications/phpast/storage/PhabricatorXHPASTDAO.php @@ -0,0 +1,8 @@ + array( 'authorPHID' => 'phid?', 'input' => 'text', + 'returnCode' => 'sint32', 'stdout' => 'text', + 'stderr' => 'text', ), ) + parent::getConfiguration(); } diff --git a/src/applications/phpast/storage/PhabricatorXHPASTViewDAO.php b/src/applications/phpast/storage/PhabricatorXHPASTViewDAO.php deleted file mode 100644 index acca5e6194..0000000000 --- a/src/applications/phpast/storage/PhabricatorXHPASTViewDAO.php +++ /dev/null @@ -1,8 +0,0 @@ - 'PhabricatorPhurlURLEditController', 'edit/(?P[1-9]\d*)/' => 'PhabricatorPhurlURLEditController', + 'comment/(?P[1-9]\d*)/' + => 'PhabricatorPhurlURLCommentController', ), ), ); } + public function getShortRoutes() { + return array( + '/u/(?P[^/]+)' => 'PhabricatorPhurlShortURLController', + '.*' => 'PhabricatorPhurlShortURLDefaultController', + ); + } + + protected function getCustomCapabilities() { + return array( + PhabricatorPhurlURLCreateCapability::CAPABILITY => array( + 'default' => PhabricatorPolicies::POLICY_USER, + ), + ); + } + } diff --git a/src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php b/src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php new file mode 100644 index 0000000000..59590e6c6a --- /dev/null +++ b/src/applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php @@ -0,0 +1,16 @@ +hasApplicationCapability( + PhabricatorPhurlURLCreateCapability::CAPABILITY); + $crumbs = parent::buildApplicationCrumbs(); $crumbs->addAction( id(new PHUIListItemView()) ->setName(pht('Shorten URL')) ->setHref($this->getApplicationURI().'url/create/') - ->setIcon('fa-plus-square')); + ->setIcon('fa-plus-square') + ->setDisabled(!$can_create) + ->setWorkflow(!$can_create)); return $crumbs; } diff --git a/src/applications/phurl/controller/PhabricatorPhurlShortURLController.php b/src/applications/phurl/controller/PhabricatorPhurlShortURLController.php new file mode 100644 index 0000000000..b4f97d6813 --- /dev/null +++ b/src/applications/phurl/controller/PhabricatorPhurlShortURLController.php @@ -0,0 +1,19 @@ +getViewer(); + $append = $request->getURIData('append'); + $main_domain_uri = PhabricatorEnv::getProductionURI('/u/'.$append); + + return id(new AphrontRedirectResponse()) + ->setIsExternal(true) + ->setURI($main_domain_uri); + } +} diff --git a/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php b/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php new file mode 100644 index 0000000000..8934d4b628 --- /dev/null +++ b/src/applications/phurl/controller/PhabricatorPhurlShortURLDefaultController.php @@ -0,0 +1,23 @@ +newDialog() + ->setTitle(pht('Invalid URL')) + ->appendParagraph( + pht('This domain can only be used to open URLs'. + ' shortened using the Phurl application. The'. + ' URL you are trying to access does not have'. + ' a Phurl URL associated with it.')); + + return id(new AphrontDialogResponse()) + ->setDialog($dialog) + ->setHTTPResponseCode(404); + } +} diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLAccessController.php b/src/applications/phurl/controller/PhabricatorPhurlURLAccessController.php index 8d15f7a37b..377b627a7e 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLAccessController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLAccessController.php @@ -3,6 +3,10 @@ final class PhabricatorPhurlURLAccessController extends PhabricatorPhurlController { + public function shouldAllowPublic() { + return true; + } + public function handleRequest(AphrontRequest $request) { $viewer = $this->getViewer(); $id = $request->getURIData('id'); @@ -32,5 +36,4 @@ final class PhabricatorPhurlURLAccessController return id(new AphrontRedirectResponse())->setURI('/'.$url->getMonogram()); } } - } diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php b/src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php new file mode 100644 index 0000000000..8726a38706 --- /dev/null +++ b/src/applications/phurl/controller/PhabricatorPhurlURLCommentController.php @@ -0,0 +1,63 @@ +isFormPost()) { + return new Aphront400Response(); + } + + $viewer = $request->getViewer(); + $id = $request->getURIData('id'); + + $is_preview = $request->isPreviewRequest(); + $draft = PhabricatorDraft::buildFromRequest($request); + + $phurl = id(new PhabricatorPhurlURLQuery()) + ->setViewer($viewer) + ->withIDs(array($id)) + ->executeOne(); + if (!$phurl) { + return new Aphront404Response(); + } + + $view_uri = '/'.$phurl->getMonogram(); + + $xactions = array(); + $xactions[] = id(new PhabricatorPhurlURLTransaction()) + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) + ->attachComment( + id(new PhabricatorPhurlURLTransactionComment()) + ->setContent($request->getStr('comment'))); + + $editor = id(new PhabricatorPhurlURLEditor()) + ->setActor($viewer) + ->setContinueOnNoEffect($request->isContinueRequest()) + ->setContentSourceFromRequest($request) + ->setIsPreview($is_preview); + + try { + $xactions = $editor->applyTransactions($phurl, $xactions); + } catch (PhabricatorApplicationTransactionNoEffectException $ex) { + return id(new PhabricatorApplicationTransactionNoEffectResponse()) + ->setCancelURI($view_uri) + ->setException($ex); + } + + if ($draft) { + $draft->replaceOrDelete(); + } + + if ($request->isAjax() && $is_preview) { + return id(new PhabricatorApplicationTransactionResponse()) + ->setViewer($viewer) + ->setTransactions($xactions) + ->setIsPreview($is_preview); + } else { + return id(new AphrontRedirectResponse()) + ->setURI($view_uri); + } + } + +} diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php index 5a7cd9686d..4136c855d8 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLEditController.php @@ -9,7 +9,6 @@ final class PhabricatorPhurlURLEditController $viewer = $this->getViewer(); $user_phid = $viewer->getPHID(); - $error_name = true; $error_long_url = true; $error_alias = null; $validation_exception = null; @@ -18,6 +17,9 @@ final class PhabricatorPhurlURLEditController $uri_query = $request->getStr('query'); if ($is_create) { + $this->requireApplicationCapability( + PhabricatorPhurlURLCreateCapability::CAPABILITY); + $url = PhabricatorPhurlURL::initializeNewPhurlURL( $viewer); $submit_label = pht('Create'); @@ -131,8 +133,6 @@ final class PhabricatorPhurlURLEditController ->setURI($url->getURI()); } catch (PhabricatorApplicationTransactionValidationException $ex) { $validation_exception = $ex; - $error_name = $ex->getShortMessage( - PhabricatorPhurlURLTransaction::TYPE_NAME); $error_long_url = $ex->getShortMessage( PhabricatorPhurlURLTransaction::TYPE_URL); $error_alias = $ex->getShortMessage( @@ -148,8 +148,7 @@ final class PhabricatorPhurlURLEditController $name = id(new AphrontFormTextControl()) ->setLabel(pht('Name')) ->setName('name') - ->setValue($name) - ->setError($error_name); + ->setValue($name); $long_url = id(new AphrontFormTextControl()) ->setLabel(pht('URL')) diff --git a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php index 7702d67b7c..014d0eefef 100644 --- a/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php +++ b/src/applications/phurl/controller/PhabricatorPhurlURLViewController.php @@ -49,7 +49,7 @@ final class PhabricatorPhurlURLViewController : pht('More Cowbell'); $draft = PhabricatorDraft::newFromUserAndKey($viewer, $url->getPHID()); $comment_uri = $this->getApplicationURI( - '/phurl/comment/'.$url->getID().'/'); + '/url/comment/'.$url->getID().'/'); $add_comment_form = id(new PhabricatorApplicationTransactionCommentView()) ->setUser($viewer) ->setObjectPHID($url->getPHID()) @@ -79,7 +79,7 @@ final class PhabricatorPhurlURLViewController $header = id(new PHUIHeaderView()) ->setUser($viewer) - ->setHeader($url->getName()) + ->setHeader($url->getDisplayName()) ->setStatus($icon, $color, $status) ->setPolicyObject($url); diff --git a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php index e7a3c7394b..ef7f547621 100644 --- a/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php +++ b/src/applications/phurl/editor/PhabricatorPhurlURLEditor.php @@ -106,22 +106,6 @@ final class PhabricatorPhurlURLEditor $errors = parent::validateTransaction($object, $type, $xactions); switch ($type) { - case PhabricatorPhurlURLTransaction::TYPE_NAME: - $missing = $this->validateIsEmptyTextField( - $object->getName(), - $xactions); - - if ($missing) { - $error = new PhabricatorApplicationTransactionValidationError( - $type, - pht('Required'), - pht('URL name is required.'), - nonempty(last($xactions), null)); - - $error->setIsMissingFieldError(true); - $errors[] = $error; - } - break; case PhabricatorPhurlURLTransaction::TYPE_ALIAS: $overdrawn = $this->validateIsTextFieldTooLong( $object->getName(), diff --git a/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php b/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php index bdb6c1959f..aca7c14dc7 100644 --- a/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php +++ b/src/applications/phurl/remarkup/PhabricatorPhurlLinkRemarkupRule.php @@ -7,7 +7,7 @@ final class PhabricatorPhurlLinkRemarkupRule extends PhutilRemarkupRule { } public function apply($text) { - // `((U123))` remarkup link to `/u/123` + // `((123))` remarkup link to `/u/123` // `((alias))` remarkup link to `/u/alias` return preg_replace_callback( '/\(\(([^ )]+)\)\)/', @@ -25,8 +25,15 @@ final class PhabricatorPhurlLinkRemarkupRule extends PhutilRemarkupRule { } $ref = $matches[1]; + $monogram = null; + $is_monogram = '/^U(?P[1-9]\d*)/'; - if (ctype_digit($ref)) { + if (preg_match($is_monogram, $ref, $monogram)) { + $phurls = id(new PhabricatorPhurlURLQuery()) + ->setViewer($viewer) + ->withIDs(array($monogram[1])) + ->execute(); + } else if (ctype_digit($ref)) { $phurls = id(new PhabricatorPhurlURLQuery()) ->setViewer($viewer) ->withIDs(array($ref)) @@ -42,16 +49,19 @@ final class PhabricatorPhurlLinkRemarkupRule extends PhutilRemarkupRule { if ($phurl) { if ($text_mode) { - return $phurl->getName().' <'.$phurl->getLongURL().'>'; + return $phurl->getDisplayName(). + ' <'. + $phurl->getRedirectURI(). + '>'; } $link = phutil_tag( 'a', array( - 'href' => $phurl->getLongURL(), + 'href' => $phurl->getRedirectURI(), 'target' => '_blank', ), - $phurl->getName()); + $phurl->getDisplayName()); return $this->getEngine()->storeText($link); } else { diff --git a/src/applications/phurl/storage/PhabricatorPhurlURL.php b/src/applications/phurl/storage/PhabricatorPhurlURL.php index 35c5382d52..34a143bc90 100644 --- a/src/applications/phurl/storage/PhabricatorPhurlURL.php +++ b/src/applications/phurl/storage/PhabricatorPhurlURL.php @@ -79,6 +79,22 @@ final class PhabricatorPhurlURL extends PhabricatorPhurlDAO return isset($allowed_protocols[$uri->getProtocol()]); } + public function getDisplayName() { + if ($this->getName()) { + return $this->getName(); + } else { + return $this->getLongURL(); + } + } + + public function getRedirectURI() { + if (strlen($this->getAlias())) { + return '/u/'.$this->getAlias(); + } else { + return '/u/'.$this->getID(); + } + } + /* -( PhabricatorPolicyInterface )----------------------------------------- */ diff --git a/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php b/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php index a3336a7971..c5ea04881b 100644 --- a/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php +++ b/src/applications/policy/rule/PhabricatorLunarPhasePolicyRule.php @@ -2,8 +2,8 @@ final class PhabricatorLunarPhasePolicyRule extends PhabricatorPolicyRule { - const PHASE_FULL = 'full'; - const PHASE_NEW = 'new'; + const PHASE_FULL = 'full'; + const PHASE_NEW = 'new'; const PHASE_WAXING = 'waxing'; const PHASE_WANING = 'waning'; @@ -27,9 +27,9 @@ final class PhabricatorLunarPhasePolicyRule extends PhabricatorPolicyRule { return $moon->isWaxing(); case 'waning': return $moon->isWaning(); + default: + return false; } - - return false; } public function getValueControlType() { @@ -39,8 +39,8 @@ final class PhabricatorLunarPhasePolicyRule extends PhabricatorPolicyRule { public function getValueControlTemplate() { return array( 'options' => array( - self::PHASE_FULL => pht('is full'), - self::PHASE_NEW => pht('is new'), + self::PHASE_FULL => pht('is full'), + self::PHASE_NEW => pht('is new'), self::PHASE_WAXING => pht('is waxing'), self::PHASE_WANING => pht('is waning'), ), diff --git a/src/applications/project/controller/PhabricatorProjectController.php b/src/applications/project/controller/PhabricatorProjectController.php index a6901a7222..ac406412ad 100644 --- a/src/applications/project/controller/PhabricatorProjectController.php +++ b/src/applications/project/controller/PhabricatorProjectController.php @@ -70,11 +70,11 @@ abstract class PhabricatorProjectController extends PhabricatorController { $nav->setIconNav(true); $nav->setBaseURI(new PhutilURI($this->getApplicationURI())); $nav->addIcon("profile/{$id}/", $name, null, $picture); - $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon); $class = 'PhabricatorManiphestApplication'; if (PhabricatorApplication::isClassInstalledForViewer($class, $viewer)) { $phid = $project->getPHID(); + $nav->addIcon("board/{$id}/", pht('Workboard'), $board_icon); $query_uri = urisprintf( '/maniphest/?statuses=open()&projects=%s#R', $phid); diff --git a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php b/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php similarity index 95% rename from src/applications/policy/rule/PhabricatorProjectsPolicyRule.php rename to src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php index c3fbfd4f7e..1782b62a9d 100644 --- a/src/applications/policy/rule/PhabricatorProjectsPolicyRule.php +++ b/src/applications/project/policyrule/PhabricatorProjectsPolicyRule.php @@ -1,6 +1,7 @@ array( 'columns' => array('branchID', 'pathID', 'commitID'), + 'unique' => true, ), )); diff --git a/src/applications/system/engine/PhabricatorDestructionEngine.php b/src/applications/system/engine/PhabricatorDestructionEngine.php index fd7ba7c30a..607c526c14 100644 --- a/src/applications/system/engine/PhabricatorDestructionEngine.php +++ b/src/applications/system/engine/PhabricatorDestructionEngine.php @@ -92,6 +92,10 @@ final class PhabricatorDestructionEngine extends Phobject { $token->delete(); } } + + if ($object instanceof AlmanacPropertyInterface) { + $this->destroyAlmanacProperties($object_phid); + } } private function destroyEdges($src_phid) { @@ -148,4 +152,15 @@ final class PhabricatorDestructionEngine extends Phobject { $object_phid); } + private function destroyAlmanacProperties($object_phid) { + $table = new AlmanacProperty(); + $conn_w = $table->establishConnection('w'); + + queryfx( + $conn_w, + 'DELETE FROM %T WHERE objectPHID = %s', + $table->getTableName(), + $object_phid); + } + } diff --git a/src/applications/transactions/application/PhabricatorTransactionsApplication.php b/src/applications/transactions/application/PhabricatorTransactionsApplication.php index 1a8792b256..260fa889b4 100644 --- a/src/applications/transactions/application/PhabricatorTransactionsApplication.php +++ b/src/applications/transactions/application/PhabricatorTransactionsApplication.php @@ -33,6 +33,20 @@ final class PhabricatorTransactionsApplication extends PhabricatorApplication { => 'PhabricatorApplicationTransactionShowOlderController', '(?Pold|new)/(?[^/]+)/' => 'PhabricatorApplicationTransactionValueController', + 'editengine/' => array( + $this->getQueryRoutePattern() + => 'PhabricatorEditEngineListController', + '(?P[^/]+)/' => array( + $this->getQueryRoutePattern() => + 'PhabricatorEditEngineConfigurationListController', + $this->getEditRoutePattern('edit/') => + 'PhabricatorEditEngineConfigurationEditController', + 'view/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationViewController', + 'save/(?P[^/]+)/' => + 'PhabricatorEditEngineConfigurationSaveController', + ), + ), ), ); } diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php new file mode 100644 index 0000000000..1227ebc552 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationEditController.php @@ -0,0 +1,26 @@ +getViewer(); + + $target_engine_key = $request->getURIData('engineKey'); + + $target_engine = PhabricatorEditEngine::getByKey( + $viewer, + $target_engine_key); + if (!$target_engine) { + return new Aphront404Response(); + } + + $this->setEngineKey($target_engine->getEngineKey()); + + return id(new PhabricatorEditEngineConfigurationEditEngine()) + ->setTargetEngine($target_engine) + ->setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php new file mode 100644 index 0000000000..b2c18f13e5 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationListController.php @@ -0,0 +1,34 @@ +setEngineKey($request->getURIData('engineKey')); + + return id(new PhabricatorEditEngineConfigurationSearchEngine()) + ->setController($this) + ->setEngineKey($this->getEngineKey()) + ->buildResponse(); + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $engine_key = $this->getEngineKey(); + $edit_uri = "/transactions/editengine/{$engine_key}/edit/"; + + $crumbs->addAction( + id(new PHUIListItemView()) + ->setName(pht('New Form')) + ->setHref($edit_uri) + ->setIcon('fa-plus-square')); + + return $crumbs; + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php new file mode 100644 index 0000000000..f7ca08b01a --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationSaveController.php @@ -0,0 +1,55 @@ +getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $key = $request->getURIData('key'); + $viewer = $this->getViewer(); + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine_key)) + ->withIdentifiers(array($key)) + ->executeOne(); + if (!$config) { + return id(new Aphront404Response()); + } + + $view_uri = $config->getURI(); + + if ($config->getID()) { + return $this->newDialog() + ->setTitle(pht('Already Editable')) + ->appendParagraph( + pht('This form configuration is already editable.')) + ->addCancelButton($view_uri); + } + + if ($request->isFormPost()) { + $editor = id(new PhabricatorEditEngineConfigurationEditor()) + ->setActor($viewer) + ->setContentSourceFromRequest($request) + ->setContinueOnNoEffect(true); + + $editor->applyTransactions($config, array()); + + return id(new AphrontRedirectResponse()) + ->setURI($config->getURI()); + } + + // TODO: Explain what this means in more detail once the implications are + // more clear, or just link to some docs or something. + + return $this->newDialog() + ->setTitle(pht('Make Builtin Editable')) + ->appendParagraph( + pht('Make this builtin form editable?')) + ->addSubmitButton(pht('Make Editable')) + ->addCancelButton($view_uri); + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php new file mode 100644 index 0000000000..f65eed1746 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineConfigurationViewController.php @@ -0,0 +1,125 @@ +getURIData('engineKey'); + $this->setEngineKey($engine_key); + + $key = $request->getURIData('key'); + $viewer = $this->getViewer(); + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($engine_key)) + ->withIdentifiers(array($key)) + ->executeOne(); + if (!$config) { + return id(new Aphront404Response()); + } + + $is_concrete = (bool)$config->getID(); + + $actions = $this->buildActionView($config); + + $properties = $this->buildPropertyView($config) + ->setActionList($actions); + + $header = id(new PHUIHeaderView()) + ->setUser($viewer) + ->setPolicyObject($config) + ->setHeader(pht('Edit Form: %s', $config->getDisplayName())); + + $box = id(new PHUIObjectBoxView()) + ->setHeader($header) + ->addPropertyList($properties); + + $crumbs = $this->buildApplicationCrumbs(); + + if ($is_concrete) { + $crumbs->addTextCrumb(pht('Form %d', $config->getID())); + } else { + $crumbs->addTextCrumb(pht('Builtin')); + } + + if ($is_concrete) { + $timeline = $this->buildTransactionTimeline( + $config, + new PhabricatorEditEngineConfigurationTransactionQuery()); + + $timeline->setShouldTerminate(true); + } else { + $timeline = null; + } + + return $this->newPage() + ->setCrumbs($crumbs) + ->appendChild( + array( + $box, + $timeline, + )); + } + + private function buildActionView( + PhabricatorEditEngineConfiguration $config) { + $viewer = $this->getViewer(); + $engine_key = $this->getEngineKey(); + + $can_edit = PhabricatorPolicyFilter::hasCapability( + $viewer, + $config, + PhabricatorPolicyCapability::CAN_EDIT); + + $view = id(new PhabricatorActionListView()) + ->setUser($viewer); + + $key = $config->getIdentifier(); + + $base_uri = "/transactions/editengine/{$engine_key}"; + + $is_concrete = (bool)$config->getID(); + if (!$is_concrete) { + $save_uri = "{$base_uri}/save/{$key}/"; + + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Make Editable')) + ->setIcon('fa-pencil') + ->setDisabled(!$can_edit) + ->setWorkflow(true) + ->setHref($save_uri)); + + $can_edit = false; + } else { + $edit_uri = "{$base_uri}/edit/{$key}/"; + $view->addAction( + id(new PhabricatorActionView()) + ->setName(pht('Edit Form Configuration')) + ->setIcon('fa-pencil') + ->setDisabled(!$can_edit) + ->setWorkflow(!$can_edit) + ->setHref($edit_uri)); + } + + return $view; + } + + private function buildPropertyView( + PhabricatorEditEngineConfiguration $config) { + $viewer = $this->getViewer(); + + $properties = id(new PHUIPropertyListView()) + ->setUser($viewer) + ->setObject($config); + + return $properties; + } + + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineController.php b/src/applications/transactions/controller/PhabricatorEditEngineController.php new file mode 100644 index 0000000000..e920260f37 --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineController.php @@ -0,0 +1,37 @@ +engineKey = $engine_key; + return $this; + } + + public function getEngineKey() { + return $this->engineKey; + } + + protected function buildApplicationCrumbs() { + $crumbs = parent::buildApplicationCrumbs(); + + $crumbs->addTextCrumb(pht('Edit Engines'), '/transactions/editengine/'); + + $engine_key = $this->getEngineKey(); + if ($engine_key !== null) { + $engine = PhabricatorEditEngine::getByKey( + $this->getViewer(), + $engine_key); + if ($engine) { + $crumbs->addTextCrumb( + $engine->getEngineName(), + "/transactions/editengine/{$engine_key}/"); + } + } + + return $crumbs; + } + +} diff --git a/src/applications/transactions/controller/PhabricatorEditEngineListController.php b/src/applications/transactions/controller/PhabricatorEditEngineListController.php new file mode 100644 index 0000000000..3120d6846c --- /dev/null +++ b/src/applications/transactions/controller/PhabricatorEditEngineListController.php @@ -0,0 +1,16 @@ +setController($this) + ->buildResponse(); + } + +} diff --git a/src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php b/src/applications/transactions/editengine/PhabricatorEditEngine.php similarity index 74% rename from src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php rename to src/applications/transactions/editengine/PhabricatorEditEngine.php index 86a03b51f4..13c2b3e017 100644 --- a/src/applications/transactions/editengine/PhabricatorApplicationEditEngine.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngine.php @@ -4,6 +4,7 @@ /** * @task fields Managing Fields * @task text Display Text + * @task config Edit Engine Configuration * @task uri Managing URIs * @task load Creating and Loading Objects * @task web Responding to Web Requests @@ -11,11 +12,16 @@ * @task http Responding to HTTP Parameter Requests * @task conduit Responding to Conduit Requests */ -abstract class PhabricatorApplicationEditEngine extends Phobject { +abstract class PhabricatorEditEngine + extends Phobject + implements PhabricatorPolicyInterface { + + const EDITENGINECONFIG_DEFAULT = 'default'; private $viewer; private $controller; private $isCreate; + private $editEngineConfiguration; final public function setViewer(PhabricatorUser $viewer) { $this->viewer = $viewer; @@ -36,10 +42,15 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { return $this->controller; } + final public function getEngineKey() { + return $this->getPhobjectClassConstant('ENGINECONST', 64); + } + /* -( Managing Fields )---------------------------------------------------- */ + abstract public function getEngineApplicationClass(); abstract protected function buildCustomEditFields($object); final protected function buildEditFields($object) { @@ -184,6 +195,9 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } } + $config = $this->getEditEngineConfiguration(); + $fields = $config->applyConfigurationToFields($this, $fields); + foreach ($fields as $field) { $field ->setViewer($viewer) @@ -197,6 +211,12 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { /* -( Display Text )------------------------------------------------------- */ + /** + * @task text + */ + abstract public function getEngineName(); + + /** * @task text */ @@ -212,7 +232,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { /** * @task text */ - abstract protected function getObjectCreateShortText($object); + abstract protected function getObjectCreateShortText(); /** @@ -237,6 +257,121 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } +/* -( Edit Engine Configuration )------------------------------------------ */ + + + protected function supportsEditEngineConfiguration() { + return true; + } + + final protected function getEditEngineConfiguration() { + return $this->editEngineConfiguration; + } + + private function loadEditEngineConfiguration($key) { + if ($key === null) { + $key = self::EDITENGINECONFIG_DEFAULT; + } + + $config = id(new PhabricatorEditEngineConfigurationQuery()) + ->setViewer($this->getViewer()) + ->withEngineKeys(array($this->getEngineKey())) + ->withIdentifiers(array($key)) + ->executeOne(); + if (!$config) { + return null; + } + + $this->editEngineConfiguration = $config; + + return $config; + } + + final public function getBuiltinEngineConfigurations() { + $configurations = $this->newBuiltinEngineConfigurations(); + + if (!$configurations) { + throw new Exception( + pht( + 'EditEngine ("%s") returned no builtin engine configurations, but '. + 'an edit engine must have at least one configuration.', + get_class($this))); + } + + assert_instances_of($configurations, 'PhabricatorEditEngineConfiguration'); + + $has_default = false; + foreach ($configurations as $config) { + if ($config->getBuiltinKey() == self::EDITENGINECONFIG_DEFAULT) { + $has_default = true; + } + } + + if (!$has_default) { + $first = head($configurations); + if (!$first->getBuiltinKey()) { + $first->setBuiltinKey(self::EDITENGINECONFIG_DEFAULT); + + if (!strlen($first->getName())) { + $first->setName($this->getObjectCreateShortText()); + } + } else { + throw new Exception( + pht( + 'EditEngine ("%s") returned builtin engine configurations, '. + 'but none are marked as default and the first configuration has '. + 'a different builtin key already. Mark a builtin as default or '. + 'omit the key from the first configuration', + get_class($this))); + } + } + + $builtins = array(); + foreach ($configurations as $key => $config) { + $builtin_key = $config->getBuiltinKey(); + + if ($builtin_key === null) { + throw new Exception( + pht( + 'EditEngine ("%s") returned builtin engine configurations, '. + 'but one (with key "%s") is missing a builtin key. Provide a '. + 'builtin key for each configuration (you can omit it from the '. + 'first configuration in the list to automatically assign the '. + 'default key).', + get_class($this), + $key)); + } + + if (isset($builtins[$builtin_key])) { + throw new Exception( + pht( + 'EditEngine ("%s") returned builtin engine configurations, '. + 'but at least two specify the same builtin key ("%s"). Engines '. + 'must have unique builtin keys.', + get_class($this), + $builtin_key)); + } + + $builtins[$builtin_key] = $config; + } + + + return $builtins; + } + + protected function newBuiltinEngineConfigurations() { + return array( + $this->newConfiguration(), + ); + } + + final protected function newConfiguration() { + return PhabricatorEditEngineConfiguration::initializeNewConfiguration( + $this->getViewer(), + $this); + } + + /* -( Managing URIs )------------------------------------------------------ */ @@ -317,7 +452,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { * @return bool True if a new object is being created. * @task load */ - final protected function getIsCreate() { + final public function getIsCreate() { return $this->isCreate; } @@ -391,6 +526,35 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } + /** + * Verify that an object is appropriate for editing. + * + * @param wild Loaded value. + * @return void + * @task load + */ + private function validateObject($object) { + if (!$object || !is_object($object)) { + throw new Exception( + pht( + 'EditEngine "%s" created or loaded an invalid object: object must '. + 'actually be an object, but is of some other type ("%s").', + get_class($this), + gettype($object))); + } + + if (!($object instanceof PhabricatorApplicationTransactionInterface)) { + throw new Exception( + pht( + 'EditEngine "%s" created or loaded an invalid object: object (of '. + 'class "%s") must implement "%s", but does not.', + get_class($this), + get_class($object), + 'PhabricatorApplicationTransactionInterface')); + } + } + + /* -( Responding to Web Requests )----------------------------------------- */ @@ -399,6 +563,11 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $controller = $this->getController(); $request = $controller->getRequest(); + $config = $this->loadEditEngineConfiguration($request->getURIData('form')); + if (!$config) { + return new Aphront404Response(); + } + $id = $request->getURIData('id'); if ($id) { $this->setIsCreate(false); @@ -411,6 +580,8 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $object = $this->newEditableObject(); } + $this->validateObject($object); + $action = $request->getURIData('editAction'); switch ($action) { case 'parameters': @@ -425,7 +596,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $crumbs = $controller->buildApplicationCrumbsForEditEngine(); if ($this->getIsCreate()) { - $create_text = $this->getObjectCreateShortText($object); + $create_text = $this->getObjectCreateShortText(); if ($final) { $crumbs->addTextCrumb($create_text); } else { @@ -570,6 +741,20 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { private function buildEditFormActions($object) { $actions = array(); + if ($this->supportsEditEngineConfiguration()) { + $engine_key = $this->getEngineKey(); + $config = $this->getEditEngineConfiguration(); + + $actions[] = id(new PhabricatorActionView()) + ->setName(pht('Manage Form Configurations')) + ->setIcon('fa-list-ul') + ->setHref("/transactions/editengine/{$engine_key}/"); + $actions[] = id(new PhabricatorActionView()) + ->setName(pht('Edit Form Configuration')) + ->setIcon('fa-pencil') + ->setHref($config->getURI()); + } + $actions[] = id(new PhabricatorActionView()) ->setName(pht('Show HTTP Parameters')) ->setIcon('fa-crosshairs') @@ -601,7 +786,7 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $header_text = pht( 'HTTP Parameters: %s', - $this->getObjectCreateShortText($object)); + $this->getObjectCreateShortText()); $header = id(new PHUIHeaderView()) ->setHeader($header_text); @@ -637,6 +822,14 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { final public function buildConduitResponse(ConduitAPIRequest $request) { $viewer = $this->getViewer(); + $config = $this->loadEditEngineConfiguration(null); + if (!$config) { + throw new Exception( + pht( + 'Unable to load configuration for this EditEngine ("%s").', + get_class($this))); + } + $phid = $request->getValue('objectPHID'); if ($phid) { $this->setIsCreate(false); @@ -649,6 +842,8 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { $object = $this->newEditableObject(); } + $this->validateObject($object); + $fields = $this->buildEditFields($object); $types = $this->getAllEditTypesFromFields($fields); @@ -767,10 +962,56 @@ abstract class PhabricatorApplicationEditEngine extends Phobject { } public function getAllEditTypes() { + $config = $this->loadEditEngineConfiguration(null); + if (!$config) { + return array(); + } + $object = $this->newEditableObject(); $fields = $this->buildEditFields($object); return $this->getAllEditTypesFromFields($fields); } + final public static function getAllEditEngines() { + return id(new PhutilClassMapQuery()) + ->setAncestorClass(__CLASS__) + ->setUniqueMethod('getEngineKey') + ->execute(); + } + final public static function getByKey(PhabricatorUser $viewer, $key) { + return id(new PhabricatorEditEngineQuery()) + ->setViewer($viewer) + ->withEngineKeys(array($key)) + ->executeOne(); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getPHID() { + return get_class($this); + } + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return PhabricatorPolicies::getMostOpenPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } } diff --git a/src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php similarity index 95% rename from src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php rename to src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index 9fc6495455..cb7662c7ba 100644 --- a/src/applications/transactions/editengine/PhabricatorApplicationEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -1,10 +1,16 @@ newEditEngine(); + $class = $engine->getEngineApplicationClass(); + return PhabricatorApplication::getByClass($class); + } + public function getMethodStatus() { return self::METHOD_STATUS_UNSTABLE; } diff --git a/src/applications/transactions/editfield/PhabricatorEditField.php b/src/applications/transactions/editfield/PhabricatorEditField.php index bfb8529933..55815c3c4d 100644 --- a/src/applications/transactions/editfield/PhabricatorEditField.php +++ b/src/applications/transactions/editfield/PhabricatorEditField.php @@ -13,7 +13,7 @@ abstract class PhabricatorEditField extends Phobject { private $metadata = array(); private $description; private $editTypeKey; - + private $isLocked; public function setKey($key) { $this->key = $key; @@ -69,7 +69,18 @@ abstract class PhabricatorEditField extends Phobject { return $this->description; } - abstract protected function newControl(); + public function setIsLocked($is_locked) { + $this->isLocked = $is_locked; + return $this; + } + + public function getIsLocked() { + return $this->isLocked; + } + + protected function newControl() { + throw new PhutilMethodNotImplementedException(); + } protected function renderControl() { $control = $this->newControl(); @@ -85,6 +96,10 @@ abstract class PhabricatorEditField extends Phobject { $control->setLabel($this->getLabel()); } + if ($this->getIsLocked()) { + $control->setDisabled(true); + } + return $control; } @@ -166,6 +181,15 @@ abstract class PhabricatorEditField extends Phobject { return $this; } + public function readDefaultValueFromConfiguration($value) { + $this->value = $this->getDefaultValueFromConfiguration($value); + return $this; + } + + protected function getDefaultValueFromConfiguration($value) { + return $value; + } + protected function getValueFromObject($object) { if ($this->hasValue) { return $this->value; diff --git a/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php b/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php new file mode 100644 index 0000000000..9da1d49ae6 --- /dev/null +++ b/src/applications/transactions/editfield/PhabricatorInstructionsEditField.php @@ -0,0 +1,10 @@ +appendRemarkupInstructions($this->getValue()); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php index 7d05d5fda4..d61b498edd 100644 --- a/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php +++ b/src/applications/transactions/editor/PhabricatorApplicationTransactionEditor.php @@ -775,8 +775,6 @@ abstract class PhabricatorApplicationTransactionEditor throw new PhabricatorApplicationTransactionValidationException($errors); } - $file_phids = $this->extractFilePHIDs($object, $xactions); - if ($object->getID()) { foreach ($xactions as $xaction) { @@ -815,18 +813,6 @@ abstract class PhabricatorApplicationTransactionEditor $xactions = $this->filterTransactions($object, $xactions); - if (!$xactions) { - if ($read_locking) { - $object->endReadLocking(); - $read_locking = false; - } - if ($transaction_open) { - $object->killTransaction(); - $transaction_open = false; - } - return array(); - } - // Now that we've merged, filtered, and combined transactions, check for // required capabilities. foreach ($xactions as $xaction) { @@ -834,6 +820,7 @@ abstract class PhabricatorApplicationTransactionEditor } $xactions = $this->sortTransactions($xactions); + $file_phids = $this->extractFilePHIDs($object, $xactions); if ($is_preview) { $this->loadHandles($xactions); @@ -2668,7 +2655,7 @@ abstract class PhabricatorApplicationTransactionEditor $body->addRawSection(implode("\n", $headers)); foreach ($comments as $comment) { - $body->addRemarkupSection($comment); + $body->addRemarkupSection(null, $comment); } } diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php new file mode 100644 index 0000000000..e47447c351 --- /dev/null +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditEngine.php @@ -0,0 +1,82 @@ +targetEngine = $target_engine; + return $this; + } + + public function getTargetEngine() { + return $this->targetEngine; + } + + public function getEngineName() { + return pht('Edit Configurations'); + } + + public function getEngineApplicationClass() { + return 'PhabricatorTransactionsApplication'; + } + + protected function newEditableObject() { + return PhabricatorEditEngineConfiguration::initializeNewConfiguration( + $this->getViewer(), + $this->getTargetEngine()); + } + + protected function newObjectQuery() { + return id(new PhabricatorEditEngineConfigurationQuery()); + } + + protected function getObjectCreateTitleText($object) { + return pht('Create New Form'); + } + + protected function getObjectEditTitleText($object) { + return pht('Edit Form %d: %s', $object->getID(), $object->getDisplayName()); + } + + protected function getObjectEditShortText($object) { + return pht('Form %d', $object->getID()); + } + + protected function getObjectCreateShortText() { + return pht('Create Form'); + } + + protected function getObjectViewURI($object) { + $engine_key = $this->getTargetEngine()->getEngineKey(); + $id = $object->getID(); + return "/transactions/editengine/{$engine_key}/view/{$id}/"; + } + + protected function getObjectEditURI($object) { + $engine_key = $this->getTargetEngine()->getEngineKey(); + $id = $object->getID(); + return "/transactions/editengine/{$engine_key}/edit/{$id}/"; + } + + protected function getObjectCreateCancelURI($object) { + $engine_key = $this->getTargetEngine()->getEngineKey(); + return "/transactions/editengine/{$engine_key}/"; + } + + protected function buildCustomEditFields($object) { + return array( + id(new PhabricatorTextEditField()) + ->setKey('name') + ->setLabel(pht('Name')) + ->setDescription(pht('Name of the form.')) + ->setTransactionType( + PhabricatorEditEngineConfigurationTransaction::TYPE_NAME) + ->setValue($object->getName()), + ); + } + +} diff --git a/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php new file mode 100644 index 0000000000..4736e89efa --- /dev/null +++ b/src/applications/transactions/editor/PhabricatorEditEngineConfigurationEditor.php @@ -0,0 +1,98 @@ +validateIsEmptyTextField( + $object->getName(), + $xactions); + + if ($missing) { + $error = new PhabricatorApplicationTransactionValidationError( + $type, + pht('Required'), + pht('Form name is required.'), + nonempty(last($xactions), null)); + + $error->setIsMissingFieldError(true); + $errors[] = $error; + } + break; + } + + return $errors; + } + + protected function getCustomTransactionOldValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + return $object->getName(); + } + } + + protected function getCustomTransactionNewValue( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + return $xaction->getNewValue(); + } + } + + protected function applyCustomInternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + $object->setName($xaction->getNewValue()); + return; + } + + return parent::applyCustomInternalTransaction($object, $xaction); + } + + protected function applyCustomExternalTransaction( + PhabricatorLiskDAO $object, + PhabricatorApplicationTransaction $xaction) { + + switch ($xaction->getTransactionType()) { + case PhabricatorEditEngineConfigurationTransaction::TYPE_NAME: + return; + } + + return parent::applyCustomExternalTransaction($object, $xaction); + } + +} diff --git a/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php new file mode 100644 index 0000000000..b4ac0ddbfe --- /dev/null +++ b/src/applications/transactions/phid/PhabricatorEditEngineConfigurationPHIDType.php @@ -0,0 +1,43 @@ +withPHIDs($phids); + } + + public function loadHandles( + PhabricatorHandleQuery $query, + array $handles, + array $objects) { + + foreach ($handles as $phid => $handle) { + $config = $objects[$phid]; + + $id = $config->getID(); + $name = $config->getName(); + + $handle->setName($name); + $handle->setURI($config->getURI()); + } + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php new file mode 100644 index 0000000000..b1c573f775 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationQuery.php @@ -0,0 +1,183 @@ +ids = $ids; + return $this; + } + + public function withPHIDs(array $phids) { + $this->phids = $phids; + return $this; + } + + public function withEngineKeys(array $engine_keys) { + $this->engineKeys = $engine_keys; + return $this; + } + + public function withBuiltinKeys(array $builtin_keys) { + $this->builtinKeys = $builtin_keys; + return $this; + } + + public function withIdentifiers(array $identifiers) { + $this->identifiers = $identifiers; + return $this; + } + + public function newResultObject() { + return new PhabricatorEditEngineConfiguration(); + } + + protected function loadPage() { + // TODO: The logic here is a little flimsy and won't survive pagination. + // For now, I'm just not bothering with pagination since I believe it will + // take some time before any install manages to produce a large enough + // number of edit forms for any particular engine for the lack of UI + // pagination to become a problem. + + $page = $this->loadStandardPage($this->newResultObject()); + + // Now that we've loaded the real results from the database, we're going + // to load builtins from the edit engines and add them to the list. + + $engines = PhabricatorEditEngine::getAllEditEngines(); + + if ($this->engineKeys) { + $engines = array_select_keys($engines, $this->engineKeys); + } + + foreach ($engines as $engine) { + $engine->setViewer($this->getViewer()); + } + + // List all the builtins which have already been saved to the database as + // real objects. + $concrete = array(); + foreach ($page as $config) { + $builtin_key = $config->getBuiltinKey(); + if ($builtin_key !== null) { + $engine_key = $config->getEngineKey(); + $concrete[$engine_key][$builtin_key] = $config; + } + } + + $builtins = array(); + foreach ($engines as $engine_key => $engine) { + $engine_builtins = $engine->getBuiltinEngineConfigurations(); + foreach ($engine_builtins as $engine_builtin) { + $builtin_key = $engine_builtin->getBuiltinKey(); + if (isset($concrete[$engine_key][$builtin_key])) { + continue; + } else { + $builtins[] = $engine_builtin; + } + } + } + + foreach ($builtins as $builtin) { + $page[] = $builtin; + } + + // Now we have to do some extra filtering to make sure everything we're + // about to return really satisfies the query. + + if ($this->ids !== null) { + $ids = array_fuse($this->ids); + foreach ($page as $key => $config) { + if (empty($ids[$config->getID()])) { + unset($page[$key]); + } + } + } + + if ($this->phids !== null) { + $phids = array_fuse($this->phids); + foreach ($page as $key => $config) { + if (empty($phids[$config->getPHID()])) { + unset($page[$key]); + } + } + } + + if ($this->builtinKeys !== null) { + $builtin_keys = array_fuse($this->builtinKeys); + foreach ($page as $key => $config) { + if (empty($builtin_keys[$config->getBuiltinKey()])) { + unset($page[$key]); + } + } + } + + if ($this->identifiers !== null) { + $identifiers = array_fuse($this->identifiers); + foreach ($page as $key => $config) { + if (isset($identifiers[$config->getBuiltinKey()])) { + continue; + } + if (isset($identifiers[$config->getID()])) { + continue; + } + unset($page[$key]); + } + } + + return $page; + } + + protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) { + $where = parent::buildWhereClauseParts($conn); + + if ($this->ids !== null) { + $where[] = qsprintf( + $conn, + 'id IN (%Ld)', + $this->ids); + } + + if ($this->phids !== null) { + $where[] = qsprintf( + $conn, + 'phid IN (%Ls)', + $this->phids); + } + + if ($this->engineKeys !== null) { + $where[] = qsprintf( + $conn, + 'engineKey IN (%Ls)', + $this->engineKeys); + } + + if ($this->builtinKeys !== null) { + $where[] = qsprintf( + $conn, + 'builtinKey IN (%Ls)', + $this->builtinKeys); + } + + if ($this->identifiers !== null) { + $where[] = qsprintf( + $conn, + '(id IN (%Ls) OR builtinKey IN (%Ls))', + $this->identifiers, + $this->identifiers); + } + + return $where; + } + + public function getQueryApplicationClass() { + return 'PhabricatorTransactionsApplication'; + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php new file mode 100644 index 0000000000..8d55aeb5ac --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationSearchEngine.php @@ -0,0 +1,102 @@ +engineKey = $engine_key; + return $this; + } + + public function getEngineKey() { + return $this->engineKey; + } + + public function canUseInPanelContext() { + return false; + } + + public function getResultTypeDescription() { + return pht('Forms'); + } + + public function getApplicationClassName() { + return 'PhabricatorTransactionsApplication'; + } + + public function newQuery() { + return id(new PhabricatorEditEngineConfigurationQuery()) + ->withEngineKeys(array($this->getEngineKey())); + } + + protected function buildQueryFromParameters(array $map) { + $query = $this->newQuery(); + return $query; + } + + protected function buildCustomSearchFields() { + return array(); + } + + protected function getDefaultFieldOrder() { + return array(); + } + + protected function getURI($path) { + return '/transactions/editengine/'.$this->getEngineKey().'/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Forms'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $configs, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($configs, 'PhabricatorEditEngineConfiguration'); + $viewer = $this->requireViewer(); + $engine_key = $this->getEngineKey(); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer); + foreach ($configs as $config) { + $item = id(new PHUIObjectItemView()) + ->setHeader($config->getDisplayName()); + + $id = $config->getID(); + if ($id) { + $item->setObjectName(pht('Form %d', $id)); + $key = $id; + } else { + $item->setObjectName(pht('Builtin')); + $key = $config->getBuiltinKey(); + } + $item->setHref("/transactions/editengine/{$engine_key}/view/{$key}/"); + + + $list->addItem($item); + } + + return id(new PhabricatorApplicationSearchResultView()) + ->setObjectList($list); + } +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php new file mode 100644 index 0000000000..2a4677944e --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineConfigurationTransactionQuery.php @@ -0,0 +1,10 @@ +engineKeys = $keys; + return $this; + } + + protected function loadPage() { + $engines = PhabricatorEditEngine::getAllEditEngines(); + + if ($this->engineKeys !== null) { + $engines = array_select_keys($engines, $this->engineKeys); + } + + return $engines; + } + + public function getQueryApplicationClass() { + return 'PhabricatorTransactionsApplication'; + } + + protected function getResultCursor($object) { + return null; + } + +} diff --git a/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php new file mode 100644 index 0000000000..7b86d34bd9 --- /dev/null +++ b/src/applications/transactions/query/PhabricatorEditEngineSearchEngine.php @@ -0,0 +1,78 @@ +newQuery(); + return $query; + } + + protected function buildCustomSearchFields() { + return array(); + } + + protected function getDefaultFieldOrder() { + return array(); + } + + protected function getURI($path) { + return '/transactions/editengine/'.$path; + } + + protected function getBuiltinQueryNames() { + $names = array( + 'all' => pht('All Edit Engines'), + ); + + return $names; + } + + public function buildSavedQueryFromBuiltin($query_key) { + $query = $this->newSavedQuery(); + $query->setQueryKey($query_key); + + switch ($query_key) { + case 'all': + return $query; + } + + return parent::buildSavedQueryFromBuiltin($query_key); + } + + protected function renderResultList( + array $engines, + PhabricatorSavedQuery $query, + array $handles) { + assert_instances_of($engines, 'PhabricatorEditEngine'); + $viewer = $this->requireViewer(); + + $list = id(new PHUIObjectItemListView()) + ->setUser($viewer); + foreach ($engines as $engine) { + $engine_key = $engine->getEngineKey(); + $query_uri = "/transactions/editengine/{$engine_key}/"; + + $item = id(new PHUIObjectItemView()) + ->setHeader($engine->getEngineName()) + ->setHref($query_uri); + + $list->addItem($item); + } + + return id(new PhabricatorApplicationSearchResultView()) + ->setObjectList($list); + } +} diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php new file mode 100644 index 0000000000..aed81bd8a3 --- /dev/null +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfiguration.php @@ -0,0 +1,211 @@ +setEngineKey($engine->getEngineKey()) + ->attachEngine($engine) + ->setViewPolicy(PhabricatorPolicies::getMostOpenPolicy()) + ->setEditPolicy($edit_policy); + } + + public function generatePHID() { + return PhabricatorPHID::generateNewPHID( + PhabricatorEditEngineConfigurationPHIDType::TYPECONST); + } + + protected function getConfiguration() { + return array( + self::CONFIG_AUX_PHID => true, + self::CONFIG_SERIALIZATION => array( + 'properties' => self::SERIALIZATION_JSON, + ), + self::CONFIG_COLUMN_SCHEMA => array( + 'engineKey' => 'text64', + 'builtinKey' => 'text64?', + 'name' => 'text255', + 'isDisabled' => 'bool', + 'isDefault' => 'bool', + ), + self::CONFIG_KEY_SCHEMA => array( + 'key_engine' => array( + 'columns' => array('engineKey', 'builtinKey'), + 'unique' => true, + ), + 'key_default' => array( + 'columns' => array('engineKey', 'isDefault', 'isDisabled'), + ), + ), + ) + parent::getConfiguration(); + } + + public function getProperty($key, $default = null) { + return idx($this->properties, $key, $default); + } + + public function setProperty($key, $value) { + $this->properties[$key] = $value; + return $this; + } + + public function attachEngine(PhabricatorEditEngine $engine) { + $this->engine = $engine; + return $this; + } + + public function getEngine() { + return $this->assertAttached($this->engine); + } + + public function applyConfigurationToFields( + PhabricatorEditEngine $engine, + array $fields) { + $fields = mpull($fields, null, 'getKey'); + + $values = $this->getProperty('defaults', array()); + foreach ($fields as $key => $field) { + if ($engine->getIsCreate()) { + if (array_key_exists($key, $values)) { + $field->readDefaultValueFromConfiguration($values[$key]); + } + } + } + + $fields = $this->reorderFields($fields); + + $head_instructions = $this->getProperty('instructions.head'); + if (strlen($head_instructions)) { + $fields = array( + 'config.instructions.head' => id(new PhabricatorInstructionsEditField()) + ->setKey('config.instructions.head') + ->setValue($head_instructions), + ) + $fields; + } + + return $fields; + } + + private function reorderFields(array $fields) { + $keys = array(); + $fields = array_select_keys($fields, $keys) + $fields; + + // Now, move locked fields to the bottom. + $head = array(); + $tail = array(); + foreach ($fields as $key => $field) { + if (!$field->getIsLocked()) { + $head[$key] = $field; + } else { + $tail[$key] = $field; + } + } + + return $head + $tail; + } + + public function getURI() { + $engine_key = $this->getEngineKey(); + $key = $this->getIdentifier(); + + return "/transactions/editengine/{$engine_key}/view/{$key}/"; + } + + public function getIdentifier() { + $key = $this->getID(); + if (!$key) { + $key = $this->getBuiltinKey(); + } + return $key; + } + + public function getDisplayName() { + $name = $this->getName(); + if (strlen($name)) { + return $name; + } + + $builtin = $this->getBuiltinKey(); + if ($builtin !== null) { + return pht('Builtin Form "%s"', $builtin); + } + + return pht('Untitled Form'); + } + + +/* -( PhabricatorPolicyInterface )----------------------------------------- */ + + + public function getCapabilities() { + return array( + PhabricatorPolicyCapability::CAN_VIEW, + PhabricatorPolicyCapability::CAN_EDIT, + ); + } + + public function getPolicy($capability) { + switch ($capability) { + case PhabricatorPolicyCapability::CAN_VIEW: + return $this->getViewPolicy(); + case PhabricatorPolicyCapability::CAN_EDIT: + return $this->getEditPolicy(); + } + } + + public function hasAutomaticCapability($capability, PhabricatorUser $viewer) { + return false; + } + + public function describeAutomaticCapability($capability) { + return null; + } + + +/* -( PhabricatorApplicationTransactionInterface )------------------------- */ + + + public function getApplicationTransactionEditor() { + return new PhabricatorEditEngineConfigurationEditor(); + } + + public function getApplicationTransactionObject() { + return $this; + } + + public function getApplicationTransactionTemplate() { + return new PhabricatorEditEngineConfigurationTransaction(); + } + + public function willRenderTimeline( + PhabricatorApplicationTransactionView $timeline, + AphrontRequest $request) { + return $timeline; + } + +} diff --git a/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php new file mode 100644 index 0000000000..cbc7d2b911 --- /dev/null +++ b/src/applications/transactions/storage/PhabricatorEditEngineConfigurationTransaction.php @@ -0,0 +1,20 @@ +timestamp = PhabricatorTime::getNow(); + } + + public function shouldReloadDaemons() { + if ($this->timestamp < PhabricatorTime::getNow() - 10) { + return false; + } + + return $this->updateConfigVersion(); + } + + /** + * Calculate a version number for the current Phabricator configuration. + * + * The version number has no real meaning and does not provide any real + * indication of whether a configuration entry has been changed. The config + * version is intended to be a rough indicator that "something has changed", + * which indicates to the overseer that the daemons should be reloaded. + * + * @return int + */ + private function loadConfigVersion() { + $conn_r = id(new PhabricatorConfigEntry())->establishConnection('r'); + return head(queryfx_one( + $conn_r, + 'SELECT MAX(id) FROM %T', + id(new PhabricatorConfigTransaction())->getTableName())); + } + + /** + * Update the configuration version and timestamp. + * + * @return bool True if the daemons should restart, otherwise false. + */ + private function updateConfigVersion() { + $config_version = $this->loadConfigVersion(); + $this->timestamp = PhabricatorTime::getNow(); + + if (!$this->configVersion) { + $this->configVersion = $config_version; + return false; + } + + if ($this->configVersion != $config_version) { + $this->configVersion = $config_version; + return true; + } + + return false; + } + +} diff --git a/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php b/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php index 7dfafa16cc..b0aa6addd3 100644 --- a/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php +++ b/src/infrastructure/daemon/workers/query/PhabricatorWorkerArchiveTaskQuery.php @@ -41,9 +41,10 @@ final class PhabricatorWorkerArchiveTaskQuery $rows = queryfx_all( $conn_r, - 'SELECT * FROM %T %Q ORDER BY id DESC %Q', + 'SELECT * FROM %T %Q %Q %Q', $task_table->getTableName(), $this->buildWhereClause($conn_r), + $this->buildOrderClause($conn_r), $this->buildLimitClause($conn_r)); return $task_table->loadAllFromArray($rows); @@ -83,6 +84,18 @@ final class PhabricatorWorkerArchiveTaskQuery return $this->formatWhereClause($where); } + private function buildOrderClause(AphrontDatabaseConnection $conn_r) { + // NOTE: The garbage collector executes this query with a date constraint, + // and the query is inefficient if we don't use the same key for ordering. + // See T9808 for discussion. + + if ($this->dateCreatedBefore) { + return qsprintf($conn_r, 'ORDER BY dateCreated DESC, id DESC'); + } else { + return qsprintf($conn_r, 'ORDER BY id DESC'); + } + } + private function buildLimitClause(AphrontDatabaseConnection $conn_r) { $clause = ''; if ($this->limit) { diff --git a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php index 99ac7e1194..2c47c744ac 100644 --- a/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php +++ b/src/infrastructure/diff/view/PHUIDiffInlineCommentDetailView.php @@ -227,9 +227,7 @@ final class PHUIDiffInlineCommentDetailView $action_buttons = array(); if ($this->allowReply) { - if (!$is_synthetic) { - // NOTE: No product reason why you can't reply to these, but the reply // mechanism currently sends the inline comment ID to the server, not // file/line information, and synthetic comments don't have an inline @@ -242,7 +240,6 @@ final class PHUIDiffInlineCommentDetailView ->addSigil('differential-inline-reply') ->setMustCapture(true); } - } } @@ -267,11 +264,11 @@ final class PHUIDiffInlineCommentDetailView $links[] = javelin_tag( 'a', array( - 'class' => 'inline-button-divider pml msl', - 'meta' => array( + 'class' => 'inline-button-divider pml msl', + 'meta' => array( 'anchor' => $anchor_name, ), - 'sigil' => 'differential-inline-preview-jump', + 'sigil' => 'differential-inline-preview-jump', ), pht('Not Visible')); diff --git a/src/infrastructure/env/PhabricatorEnv.php b/src/infrastructure/env/PhabricatorEnv.php index b576e2f83b..694c793e0a 100644 --- a/src/infrastructure/env/PhabricatorEnv.php +++ b/src/infrastructure/env/PhabricatorEnv.php @@ -277,106 +277,6 @@ final class PhabricatorEnv extends Phobject { return $env; } - public static function calculateEnvironmentHash() { - $keys = self::getKeysForConsistencyCheck(); - - $values = array(); - foreach ($keys as $key) { - $values[$key] = self::getEnvConfigIfExists($key); - } - - return PhabricatorHash::digest(json_encode($values)); - } - - /** - * Returns a summary of non-default configuration settings to allow the - * "daemons and web have different config" setup check to list divergent - * keys. - */ - public static function calculateEnvironmentInfo() { - $keys = self::getKeysForConsistencyCheck(); - - $info = array(); - - $defaults = id(new PhabricatorConfigDefaultSource())->getAllKeys(); - foreach ($keys as $key) { - $current = self::getEnvConfigIfExists($key); - $default = idx($defaults, $key, null); - if ($current !== $default) { - $info[$key] = PhabricatorHash::digestForIndex(json_encode($current)); - } - } - - $keys_hash = array_keys($defaults); - sort($keys_hash); - $keys_hash = implode("\0", $keys_hash); - $keys_hash = PhabricatorHash::digestForIndex($keys_hash); - - return array( - 'version' => 1, - 'keys' => $keys_hash, - 'values' => $info, - ); - } - - - /** - * Compare two environment info summaries to generate a human-readable - * list of discrepancies. - */ - public static function compareEnvironmentInfo(array $u, array $v) { - $issues = array(); - - $uversion = idx($u, 'version'); - $vversion = idx($v, 'version'); - if ($uversion != $vversion) { - $issues[] = pht( - 'The two configurations were generated by different versions '. - 'of Phabricator.'); - - // These may not be comparable, so stop here. - return $issues; - } - - if ($u['keys'] !== $v['keys']) { - $issues[] = pht( - 'The two configurations have different keys. This usually means '. - 'that they are running different versions of Phabricator.'); - } - - $uval = idx($u, 'values', array()); - $vval = idx($v, 'values', array()); - - $all_keys = array_keys($uval + $vval); - - foreach ($all_keys as $key) { - $uv = idx($uval, $key); - $vv = idx($vval, $key); - if ($uv !== $vv) { - if ($uv && $vv) { - $issues[] = pht( - 'The configuration key "%s" is set in both configurations, but '. - 'set to different values.', - $key); - } else { - $issues[] = pht( - 'The configuration key "%s" is set in only one configuration.', - $key); - } - } - } - - return $issues; - } - - private static function getKeysForConsistencyCheck() { - $keys = array_keys(self::getAllConfigKeys()); - sort($keys); - - $skip_keys = self::getEnvConfig('phd.variant-config'); - return array_diff($keys, $skip_keys); - } - /* -( Reading Configuration )---------------------------------------------- */ diff --git a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php index fd529dc35d..346e37977d 100644 --- a/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php +++ b/src/infrastructure/internationalization/translation/PhabricatorUSEnglishTranslation.php @@ -178,14 +178,14 @@ final class PhabricatorUSEnglishTranslation ), ), - '%s merged %d task(s): %s.' => array( + '%s merged %s task(s): %s.' => array( array( '%s merged a task: %3$s.', '%s merged tasks: %3$s.', ), ), - '%s merged %d task(s) %s into %s.' => array( + '%s merged %s task(s) %s into %s.' => array( array( '%s merged %3$s into %4$s.', '%s merged tasks %3$s into %4$s.', diff --git a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php index 62b2651f70..3203998a40 100644 --- a/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php +++ b/src/infrastructure/storage/management/workflow/PhabricatorStorageManagementWorkflow.php @@ -633,7 +633,7 @@ abstract class PhabricatorStorageManagementWorkflow if ($any_surplus) { $message[] = pht( 'Some of these errors are caused by surplus schemata (extra '. - 'tables or columsn which Phabricator does not expect). These are '. + 'tables or columns which Phabricator does not expect). These are '. 'not serious. For information on resolving these issues, see the '. '"Surplus Schemata" section in the "Managing Storage Adjustments" '. 'article in the documentation.'); diff --git a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php index ef4c24eba1..ce3d8da981 100644 --- a/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php +++ b/src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php @@ -79,7 +79,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList { ), 'db.user' => array(), 'db.worker' => array(), - 'db.xhpastview' => array(), + 'db.xhpast' => array(), + 'db.xhpastview' => array( + 'dead' => true, + ), 'db.cache' => array(), 'db.fact' => array(), 'db.ponder' => array(), diff --git a/src/infrastructure/testing/PhabricatorTestCase.php b/src/infrastructure/testing/PhabricatorTestCase.php index 32fe4f3bee..068c2ed557 100644 --- a/src/infrastructure/testing/PhabricatorTestCase.php +++ b/src/infrastructure/testing/PhabricatorTestCase.php @@ -120,6 +120,10 @@ abstract class PhabricatorTestCase extends PhutilTestCase { 'phabricator.base-uri', 'http://phabricator.example.com'); + $this->env->overrideEnvConfig( + 'auth.email-domains', + array()); + // Tests do their own stubbing/voiding for events. $this->env->overrideEnvConfig('phabricator.silent', false); } diff --git a/webroot/rsrc/css/phui/phui-crumbs-view.css b/webroot/rsrc/css/phui/phui-crumbs-view.css index 2f38e0ca47..71113dadc9 100644 --- a/webroot/rsrc/css/phui/phui-crumbs-view.css +++ b/webroot/rsrc/css/phui/phui-crumbs-view.css @@ -25,7 +25,8 @@ padding-right: 0; } -.phui-crumbs-view a.phui-crumbs-action-disabled { +.phui-crumbs-view a.phui-crumbs-action-disabled, +.phui-crumbs-view a.phui-crumbs-action-disabled .phui-icon-view { color: {$lightgreytext}; } diff --git a/webroot/rsrc/css/phui/phui-document-pro.css b/webroot/rsrc/css/phui/phui-document-pro.css index 3408c5b094..47aa677105 100644 --- a/webroot/rsrc/css/phui/phui-document-pro.css +++ b/webroot/rsrc/css/phui/phui-document-pro.css @@ -112,3 +112,64 @@ a.button.phui-document-toc { .phui-document-view-pro .phui-info-view { margin: 16px 0 0 0; } + + + + +.phui-document-view-pro-box .phui-timeline-view { + padding: 0; + background: none; +} + +.phui-document-view-pro-box .phui-timeline-image { + border-radius: 25px; +} + +.phui-document-view-pro-box .phui-timeline-wedge { + display: none; +} + +.phui-document-view-pro-box .phui-timeline-major-event .phui-timeline-group { + border: none; +} + +.phui-document-view-pro-box .phui-timeline-major-event .phui-timeline-content { + border: none; +} + +.device-desktop .phui-document-view-pro-box + .phui-timeline-event-view.phui-timeline-minor-event { + margin-left: 62px; +} + +.phui-document-view-pro-box .phui-timeline-title { + border-radius: 3px; + background-color: {$lightgreybackground}; +} + +.phui-document-view-pro-box .phui-timeline-title-with-icon { + padding-left: 12px; +} + +.phui-document-view-pro-box .phui-timeline-icon-fill { + display: none; +} + +.phui-document-view-pro-box .phui-timeline-major-event .phui-timeline-content + .phui-timeline-core-content { + padding-bottom: 24px; +} + +.phui-document-view-pro-box .phui-object-box { + background-color: {$lightgreybackground}; + border: none; + margin: 0; +} + +.phui-document-view-pro-box .phui-object-box .phui-form-view { + padding-bottom: 0; +} + +.phui-document-view-pro-box .phui-object-box .remarkup-assist-textarea { + height: 9em; +}