(stable) Promote 2017 Week 16
Before Width: | Height: | Size: 993 B After Width: | Height: | Size: 293 B |
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 360 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 375 B |
Before Width: | Height: | Size: 1.3 KiB After Width: | Height: | Size: 370 B |
BIN
resources/builtin/image-400x400.png
Normal file
After Width: | Height: | Size: 456 B |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 546 B |
BIN
resources/builtin/image-800x800.png
Normal file
After Width: | Height: | Size: 666 B |
|
@ -46,6 +46,7 @@ return array(
|
|||
'javelin-behavior-toggle-class',
|
||||
'javelin-behavior-lightbox-attachments',
|
||||
'phabricator-busy',
|
||||
'javelin-sound',
|
||||
'javelin-aphlict',
|
||||
'phabricator-notification',
|
||||
'javelin-behavior-aphlict-listen',
|
||||
|
@ -160,6 +161,7 @@ return array(
|
|||
'conpherence.pkg.css' => array(
|
||||
'conpherence-durable-column-view',
|
||||
'conpherence-menu-css',
|
||||
'conpherence-color-css',
|
||||
'conpherence-message-pane-css',
|
||||
'conpherence-notification-css',
|
||||
'conpherence-transaction-css',
|
||||
|
|
7
resources/sql/autopatches/20170417.files.ngrams.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE {$NAMESPACE}_file.file_filename_ngrams (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
objectID INT UNSIGNED NOT NULL,
|
||||
ngram CHAR(3) NOT NULL COLLATE {$COLLATE_TEXT},
|
||||
KEY `key_object` (objectID),
|
||||
KEY `key_ngram` (ngram, objectID)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,19 @@
|
|||
CREATE TABLE {$NAMESPACE}_application.application_applicationtransaction (
|
||||
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};
|
16
resources/sql/autopatches/20170418.1.application.02.edge.sql
Normal file
|
@ -0,0 +1,16 @@
|
|||
CREATE TABLE {$NAMESPACE}_application.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}_application.edgedata (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
data LONGTEXT NOT NULL COLLATE {$COLLATE_TEXT}
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20170418.files.isDeleted.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_file.file
|
||||
ADD isDeleted BOOL NOT NULL DEFAULT 0;
|
7
resources/sql/autopatches/20170419.app.01.table.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
CREATE TABLE {$NAMESPACE}_application.application_application (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
|
||||
phid VARBINARY(64) NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid)
|
||||
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
|
2
resources/sql/autopatches/20170419.thread.01.behind.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||
DROP behindTransactionPHID;
|
2
resources/sql/autopatches/20170419.thread.02.status.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||
DROP participationStatus;
|
2
resources/sql/autopatches/20170419.thread.03.touched.sql
Normal file
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_conpherence.conpherence_participant
|
||||
DROP dateTouched;
|
|
@ -305,12 +305,13 @@ phutil_register_library_map(array(
|
|||
'ConpherenceParticipantCountQuery' => 'applications/conpherence/query/ConpherenceParticipantCountQuery.php',
|
||||
'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php',
|
||||
'ConpherenceParticipantView' => 'applications/conpherence/view/ConpherenceParticipantView.php',
|
||||
'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php',
|
||||
'ConpherenceQueryThreadConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryThreadConduitAPIMethod.php',
|
||||
'ConpherenceQueryTransactionConduitAPIMethod' => 'applications/conpherence/conduit/ConpherenceQueryTransactionConduitAPIMethod.php',
|
||||
'ConpherenceReplyHandler' => 'applications/conpherence/mail/ConpherenceReplyHandler.php',
|
||||
'ConpherenceRoomListController' => 'applications/conpherence/controller/ConpherenceRoomListController.php',
|
||||
'ConpherenceRoomPictureController' => 'applications/conpherence/controller/ConpherenceRoomPictureController.php',
|
||||
'ConpherenceRoomPreferencesController' => 'applications/conpherence/controller/ConpherenceRoomPreferencesController.php',
|
||||
'ConpherenceRoomSettings' => 'applications/conpherence/constants/ConpherenceRoomSettings.php',
|
||||
'ConpherenceRoomTestCase' => 'applications/conpherence/__tests__/ConpherenceRoomTestCase.php',
|
||||
'ConpherenceSchemaSpec' => 'applications/conpherence/storage/ConpherenceSchemaSpec.php',
|
||||
'ConpherenceTestCase' => 'applications/conpherence/__tests__/ConpherenceTestCase.php',
|
||||
|
@ -321,6 +322,7 @@ phutil_register_library_map(array(
|
|||
'ConpherenceThreadListView' => 'applications/conpherence/view/ConpherenceThreadListView.php',
|
||||
'ConpherenceThreadMailReceiver' => 'applications/conpherence/mail/ConpherenceThreadMailReceiver.php',
|
||||
'ConpherenceThreadMembersPolicyRule' => 'applications/conpherence/policyrule/ConpherenceThreadMembersPolicyRule.php',
|
||||
'ConpherenceThreadParticipantsTransaction' => 'applications/conpherence/xaction/ConpherenceThreadParticipantsTransaction.php',
|
||||
'ConpherenceThreadPictureTransaction' => 'applications/conpherence/xaction/ConpherenceThreadPictureTransaction.php',
|
||||
'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php',
|
||||
'ConpherenceThreadRemarkupRule' => 'applications/conpherence/remarkup/ConpherenceThreadRemarkupRule.php',
|
||||
|
@ -349,6 +351,7 @@ phutil_register_library_map(array(
|
|||
'DarkConsoleEventPlugin' => 'applications/console/plugin/DarkConsoleEventPlugin.php',
|
||||
'DarkConsoleEventPluginAPI' => 'applications/console/plugin/event/DarkConsoleEventPluginAPI.php',
|
||||
'DarkConsolePlugin' => 'applications/console/plugin/DarkConsolePlugin.php',
|
||||
'DarkConsoleRealtimePlugin' => 'applications/console/plugin/DarkConsoleRealtimePlugin.php',
|
||||
'DarkConsoleRequestPlugin' => 'applications/console/plugin/DarkConsoleRequestPlugin.php',
|
||||
'DarkConsoleServicesPlugin' => 'applications/console/plugin/DarkConsoleServicesPlugin.php',
|
||||
'DarkConsoleStartupPlugin' => 'applications/console/plugin/DarkConsoleStartupPlugin.php',
|
||||
|
@ -1097,6 +1100,7 @@ phutil_register_library_map(array(
|
|||
'FileAllocateConduitAPIMethod' => 'applications/files/conduit/FileAllocateConduitAPIMethod.php',
|
||||
'FileConduitAPIMethod' => 'applications/files/conduit/FileConduitAPIMethod.php',
|
||||
'FileCreateMailReceiver' => 'applications/files/mail/FileCreateMailReceiver.php',
|
||||
'FileDeletionWorker' => 'applications/files/worker/FileDeletionWorker.php',
|
||||
'FileDownloadConduitAPIMethod' => 'applications/files/conduit/FileDownloadConduitAPIMethod.php',
|
||||
'FileInfoConduitAPIMethod' => 'applications/files/conduit/FileInfoConduitAPIMethod.php',
|
||||
'FileMailReceiver' => 'applications/files/mail/FileMailReceiver.php',
|
||||
|
@ -1835,6 +1839,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAppSearchEngine' => 'applications/meta/query/PhabricatorAppSearchEngine.php',
|
||||
'PhabricatorApplication' => 'applications/base/PhabricatorApplication.php',
|
||||
'PhabricatorApplicationApplicationPHIDType' => 'applications/meta/phid/PhabricatorApplicationApplicationPHIDType.php',
|
||||
'PhabricatorApplicationApplicationTransaction' => 'applications/meta/storage/PhabricatorApplicationApplicationTransaction.php',
|
||||
'PhabricatorApplicationApplicationTransactionQuery' => 'applications/meta/query/PhabricatorApplicationApplicationTransactionQuery.php',
|
||||
'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php',
|
||||
'PhabricatorApplicationConfigurationPanel' => 'applications/meta/panel/PhabricatorApplicationConfigurationPanel.php',
|
||||
'PhabricatorApplicationConfigurationPanelTestCase' => 'applications/meta/panel/__tests__/PhabricatorApplicationConfigurationPanelTestCase.php',
|
||||
|
@ -1846,6 +1852,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationPanelController' => 'applications/meta/controller/PhabricatorApplicationPanelController.php',
|
||||
'PhabricatorApplicationProfileMenuItem' => 'applications/search/menuitem/PhabricatorApplicationProfileMenuItem.php',
|
||||
'PhabricatorApplicationQuery' => 'applications/meta/query/PhabricatorApplicationQuery.php',
|
||||
'PhabricatorApplicationSchemaSpec' => 'applications/meta/storage/PhabricatorApplicationSchemaSpec.php',
|
||||
'PhabricatorApplicationSearchController' => 'applications/search/controller/PhabricatorApplicationSearchController.php',
|
||||
'PhabricatorApplicationSearchEngine' => 'applications/search/engine/PhabricatorApplicationSearchEngine.php',
|
||||
'PhabricatorApplicationSearchEngineTestCase' => 'applications/search/engine/__tests__/PhabricatorApplicationSearchEngineTestCase.php',
|
||||
|
@ -1878,6 +1885,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionTemplatedCommentQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionTemplatedCommentQuery.php',
|
||||
'PhabricatorApplicationTransactionTextDiffDetailView' => 'applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php',
|
||||
'PhabricatorApplicationTransactionTransactionPHIDType' => 'applications/transactions/phid/PhabricatorApplicationTransactionTransactionPHIDType.php',
|
||||
'PhabricatorApplicationTransactionType' => 'applications/meta/xactions/PhabricatorApplicationTransactionType.php',
|
||||
'PhabricatorApplicationTransactionValidationError' => 'applications/transactions/error/PhabricatorApplicationTransactionValidationError.php',
|
||||
'PhabricatorApplicationTransactionValidationException' => 'applications/transactions/exception/PhabricatorApplicationTransactionValidationException.php',
|
||||
'PhabricatorApplicationTransactionValidationResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionValidationResponse.php',
|
||||
|
@ -2081,20 +2089,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBoardRenderingEngine' => 'applications/project/engine/PhabricatorBoardRenderingEngine.php',
|
||||
'PhabricatorBoardResponseEngine' => 'applications/project/engine/PhabricatorBoardResponseEngine.php',
|
||||
'PhabricatorBoolEditField' => 'applications/transactions/editfield/PhabricatorBoolEditField.php',
|
||||
'PhabricatorBot' => 'infrastructure/daemon/bot/PhabricatorBot.php',
|
||||
'PhabricatorBotChannel' => 'infrastructure/daemon/bot/target/PhabricatorBotChannel.php',
|
||||
'PhabricatorBotDebugLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotDebugLogHandler.php',
|
||||
'PhabricatorBotFeedNotificationHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotFeedNotificationHandler.php',
|
||||
'PhabricatorBotFlowdockProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorBotFlowdockProtocolAdapter.php',
|
||||
'PhabricatorBotHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotHandler.php',
|
||||
'PhabricatorBotLogHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotLogHandler.php',
|
||||
'PhabricatorBotMacroHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotMacroHandler.php',
|
||||
'PhabricatorBotMessage' => 'infrastructure/daemon/bot/PhabricatorBotMessage.php',
|
||||
'PhabricatorBotObjectNameHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotObjectNameHandler.php',
|
||||
'PhabricatorBotSymbolHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotSymbolHandler.php',
|
||||
'PhabricatorBotTarget' => 'infrastructure/daemon/bot/target/PhabricatorBotTarget.php',
|
||||
'PhabricatorBotUser' => 'infrastructure/daemon/bot/target/PhabricatorBotUser.php',
|
||||
'PhabricatorBotWhatsNewHandler' => 'infrastructure/daemon/bot/handler/PhabricatorBotWhatsNewHandler.php',
|
||||
'PhabricatorBritishEnglishTranslation' => 'infrastructure/internationalization/translation/PhabricatorBritishEnglishTranslation.php',
|
||||
'PhabricatorBuiltinDraftEngine' => 'applications/transactions/draft/PhabricatorBuiltinDraftEngine.php',
|
||||
'PhabricatorBuiltinPatchList' => 'infrastructure/storage/patch/PhabricatorBuiltinPatchList.php',
|
||||
|
@ -2253,7 +2247,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarRemarkupRule' => 'applications/calendar/remarkup/PhabricatorCalendarRemarkupRule.php',
|
||||
'PhabricatorCalendarReplyHandler' => 'applications/calendar/mail/PhabricatorCalendarReplyHandler.php',
|
||||
'PhabricatorCalendarSchemaSpec' => 'applications/calendar/storage/PhabricatorCalendarSchemaSpec.php',
|
||||
'PhabricatorCampfireProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorCampfireProtocolAdapter.php',
|
||||
'PhabricatorCelerityApplication' => 'applications/celerity/application/PhabricatorCelerityApplication.php',
|
||||
'PhabricatorCelerityTestCase' => '__tests__/PhabricatorCelerityTestCase.php',
|
||||
'PhabricatorChangeParserTestCase' => 'applications/repository/worker/__tests__/PhabricatorChangeParserTestCase.php',
|
||||
|
@ -2396,6 +2389,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConpherenceNotificationsSetting' => 'applications/settings/setting/PhabricatorConpherenceNotificationsSetting.php',
|
||||
'PhabricatorConpherencePreferencesSettingsPanel' => 'applications/settings/panel/PhabricatorConpherencePreferencesSettingsPanel.php',
|
||||
'PhabricatorConpherenceProfileMenuItem' => 'applications/search/menuitem/PhabricatorConpherenceProfileMenuItem.php',
|
||||
'PhabricatorConpherenceSoundSetting' => 'applications/settings/setting/PhabricatorConpherenceSoundSetting.php',
|
||||
'PhabricatorConpherenceThreadPHIDType' => 'applications/conpherence/phid/PhabricatorConpherenceThreadPHIDType.php',
|
||||
'PhabricatorConpherenceWidgetVisibleSetting' => 'applications/settings/setting/PhabricatorConpherenceWidgetVisibleSetting.php',
|
||||
'PhabricatorConsoleApplication' => 'applications/console/application/PhabricatorConsoleApplication.php',
|
||||
|
@ -2762,6 +2756,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileDAO' => 'applications/files/storage/PhabricatorFileDAO.php',
|
||||
'PhabricatorFileDataController' => 'applications/files/controller/PhabricatorFileDataController.php',
|
||||
'PhabricatorFileDeleteController' => 'applications/files/controller/PhabricatorFileDeleteController.php',
|
||||
'PhabricatorFileDeleteTransaction' => 'applications/files/xaction/PhabricatorFileDeleteTransaction.php',
|
||||
'PhabricatorFileDropUploadController' => 'applications/files/controller/PhabricatorFileDropUploadController.php',
|
||||
'PhabricatorFileEditController' => 'applications/files/controller/PhabricatorFileEditController.php',
|
||||
'PhabricatorFileEditEngine' => 'applications/files/editor/PhabricatorFileEditEngine.php',
|
||||
|
@ -2780,6 +2775,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileLightboxController' => 'applications/files/controller/PhabricatorFileLightboxController.php',
|
||||
'PhabricatorFileLinkView' => 'view/layout/PhabricatorFileLinkView.php',
|
||||
'PhabricatorFileListController' => 'applications/files/controller/PhabricatorFileListController.php',
|
||||
'PhabricatorFileNameNgrams' => 'applications/files/storage/PhabricatorFileNameNgrams.php',
|
||||
'PhabricatorFileNameTransaction' => 'applications/files/xaction/PhabricatorFileNameTransaction.php',
|
||||
'PhabricatorFileQuery' => 'applications/files/query/PhabricatorFileQuery.php',
|
||||
'PhabricatorFileROT13StorageFormat' => 'applications/files/format/PhabricatorFileROT13StorageFormat.php',
|
||||
|
@ -2910,7 +2906,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorHovercardEngineExtensionModule' => 'applications/search/engineextension/PhabricatorHovercardEngineExtensionModule.php',
|
||||
'PhabricatorIDsSearchEngineExtension' => 'applications/search/engineextension/PhabricatorIDsSearchEngineExtension.php',
|
||||
'PhabricatorIDsSearchField' => 'applications/search/field/PhabricatorIDsSearchField.php',
|
||||
'PhabricatorIRCProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorIRCProtocolAdapter.php',
|
||||
'PhabricatorIconDatasource' => 'applications/files/typeahead/PhabricatorIconDatasource.php',
|
||||
'PhabricatorIconRemarkupRule' => 'applications/macro/markup/PhabricatorIconRemarkupRule.php',
|
||||
'PhabricatorIconSet' => 'applications/files/iconset/PhabricatorIconSet.php',
|
||||
|
@ -2924,6 +2919,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorIndexEngine' => 'applications/search/index/PhabricatorIndexEngine.php',
|
||||
'PhabricatorIndexEngineExtension' => 'applications/search/index/PhabricatorIndexEngineExtension.php',
|
||||
'PhabricatorIndexEngineExtensionModule' => 'applications/search/index/PhabricatorIndexEngineExtensionModule.php',
|
||||
'PhabricatorIndexableInterface' => 'applications/search/interface/PhabricatorIndexableInterface.php',
|
||||
'PhabricatorInfrastructureTestCase' => '__tests__/PhabricatorInfrastructureTestCase.php',
|
||||
'PhabricatorInlineCommentController' => 'infrastructure/diff/PhabricatorInlineCommentController.php',
|
||||
'PhabricatorInlineCommentInterface' => 'infrastructure/diff/interface/PhabricatorInlineCommentInterface.php',
|
||||
|
@ -3445,7 +3441,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPhurlURL' => 'applications/phurl/storage/PhabricatorPhurlURL.php',
|
||||
'PhabricatorPhurlURLAccessController' => 'applications/phurl/controller/PhabricatorPhurlURLAccessController.php',
|
||||
'PhabricatorPhurlURLAliasTransaction' => 'applications/phurl/xaction/PhabricatorPhurlURLAliasTransaction.php',
|
||||
'PhabricatorPhurlURLCommentController' => 'applications/phurl/controller/PhabricatorPhurlURLCommentController.php',
|
||||
'PhabricatorPhurlURLCreateCapability' => 'applications/phurl/capability/PhabricatorPhurlURLCreateCapability.php',
|
||||
'PhabricatorPhurlURLDatasource' => 'applications/phurl/typeahead/PhabricatorPhurlURLDatasource.php',
|
||||
'PhabricatorPhurlURLDescriptionTransaction' => 'applications/phurl/xaction/PhabricatorPhurlURLDescriptionTransaction.php',
|
||||
|
@ -3643,7 +3638,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsSearchEngineExtension' => 'applications/project/engineextension/PhabricatorProjectsSearchEngineExtension.php',
|
||||
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'applications/project/engineextension/PhabricatorProjectsWatchersSearchEngineAttachment.php',
|
||||
'PhabricatorPronounSetting' => 'applications/settings/setting/PhabricatorPronounSetting.php',
|
||||
'PhabricatorProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorProtocolAdapter.php',
|
||||
'PhabricatorPygmentSetupCheck' => 'applications/config/check/PhabricatorPygmentSetupCheck.php',
|
||||
'PhabricatorQuery' => 'infrastructure/query/PhabricatorQuery.php',
|
||||
'PhabricatorQueryConstraint' => 'infrastructure/query/constraint/PhabricatorQueryConstraint.php',
|
||||
|
@ -3967,7 +3961,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStoragePatch' => 'infrastructure/storage/management/PhabricatorStoragePatch.php',
|
||||
'PhabricatorStorageSchemaSpec' => 'infrastructure/storage/schema/PhabricatorStorageSchemaSpec.php',
|
||||
'PhabricatorStorageSetupCheck' => 'applications/config/check/PhabricatorStorageSetupCheck.php',
|
||||
'PhabricatorStreamingProtocolAdapter' => 'infrastructure/daemon/bot/adapter/PhabricatorStreamingProtocolAdapter.php',
|
||||
'PhabricatorStringListEditField' => 'applications/transactions/editfield/PhabricatorStringListEditField.php',
|
||||
'PhabricatorStringSetting' => 'applications/settings/setting/PhabricatorStringSetting.php',
|
||||
'PhabricatorSubmitEditField' => 'applications/transactions/editfield/PhabricatorSubmitEditField.php',
|
||||
|
@ -5093,12 +5086,13 @@ phutil_register_library_map(array(
|
|||
'ConpherenceParticipantCountQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'ConpherenceParticipantView' => 'AphrontView',
|
||||
'ConpherenceParticipationStatus' => 'ConpherenceConstants',
|
||||
'ConpherenceQueryThreadConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
|
||||
'ConpherenceQueryTransactionConduitAPIMethod' => 'ConpherenceConduitAPIMethod',
|
||||
'ConpherenceReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'ConpherenceRoomListController' => 'ConpherenceController',
|
||||
'ConpherenceRoomPictureController' => 'ConpherenceController',
|
||||
'ConpherenceRoomPreferencesController' => 'ConpherenceController',
|
||||
'ConpherenceRoomSettings' => 'ConpherenceConstants',
|
||||
'ConpherenceRoomTestCase' => 'ConpherenceTestCase',
|
||||
'ConpherenceSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'ConpherenceTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -5116,6 +5110,7 @@ phutil_register_library_map(array(
|
|||
'ConpherenceThreadListView' => 'AphrontView',
|
||||
'ConpherenceThreadMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
'ConpherenceThreadMembersPolicyRule' => 'PhabricatorPolicyRule',
|
||||
'ConpherenceThreadParticipantsTransaction' => 'ConpherenceThreadTransactionType',
|
||||
'ConpherenceThreadPictureTransaction' => 'ConpherenceThreadTransactionType',
|
||||
'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'ConpherenceThreadRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
|
@ -5144,6 +5139,7 @@ phutil_register_library_map(array(
|
|||
'DarkConsoleEventPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleEventPluginAPI' => 'PhabricatorEventListener',
|
||||
'DarkConsolePlugin' => 'Phobject',
|
||||
'DarkConsoleRealtimePlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleRequestPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleServicesPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleStartupPlugin' => 'DarkConsolePlugin',
|
||||
|
@ -5974,6 +5970,7 @@ phutil_register_library_map(array(
|
|||
'FileAllocateConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileConduitAPIMethod' => 'ConduitAPIMethod',
|
||||
'FileCreateMailReceiver' => 'PhabricatorMailReceiver',
|
||||
'FileDeletionWorker' => 'PhabricatorWorker',
|
||||
'FileDownloadConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileInfoConduitAPIMethod' => 'FileConduitAPIMethod',
|
||||
'FileMailReceiver' => 'PhabricatorObjectMailReceiver',
|
||||
|
@ -6838,10 +6835,13 @@ phutil_register_library_map(array(
|
|||
'PhabricatorAphrontViewTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorAppSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'PhabricatorApplication' => array(
|
||||
'Phobject',
|
||||
'PhabricatorLiskDAO',
|
||||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorApplicationTransactionInterface',
|
||||
),
|
||||
'PhabricatorApplicationApplicationPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorApplicationApplicationTransaction' => 'PhabricatorModularTransaction',
|
||||
'PhabricatorApplicationApplicationTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'PhabricatorApplicationConfigOptions' => 'Phobject',
|
||||
'PhabricatorApplicationConfigurationPanel' => 'Phobject',
|
||||
'PhabricatorApplicationConfigurationPanelTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -6853,6 +6853,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationPanelController' => 'PhabricatorApplicationsController',
|
||||
'PhabricatorApplicationProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
'PhabricatorApplicationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorApplicationSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorApplicationSearchController' => 'PhabricatorSearchBaseController',
|
||||
'PhabricatorApplicationSearchEngine' => 'Phobject',
|
||||
'PhabricatorApplicationSearchEngineTestCase' => 'PhabricatorTestCase',
|
||||
|
@ -6893,6 +6894,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationTransactionTemplatedCommentQuery' => 'PhabricatorApplicationTransactionCommentQuery',
|
||||
'PhabricatorApplicationTransactionTextDiffDetailView' => 'AphrontView',
|
||||
'PhabricatorApplicationTransactionTransactionPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorApplicationTransactionType' => 'PhabricatorModularTransactionType',
|
||||
'PhabricatorApplicationTransactionValidationError' => 'Phobject',
|
||||
'PhabricatorApplicationTransactionValidationException' => 'Exception',
|
||||
'PhabricatorApplicationTransactionValidationResponse' => 'AphrontProxyResponse',
|
||||
|
@ -7130,20 +7132,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorBoardRenderingEngine' => 'Phobject',
|
||||
'PhabricatorBoardResponseEngine' => 'Phobject',
|
||||
'PhabricatorBoolEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorBot' => 'PhabricatorDaemon',
|
||||
'PhabricatorBotChannel' => 'PhabricatorBotTarget',
|
||||
'PhabricatorBotDebugLogHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotFeedNotificationHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotFlowdockProtocolAdapter' => 'PhabricatorStreamingProtocolAdapter',
|
||||
'PhabricatorBotHandler' => 'Phobject',
|
||||
'PhabricatorBotLogHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotMacroHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotMessage' => 'Phobject',
|
||||
'PhabricatorBotObjectNameHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotSymbolHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBotTarget' => 'Phobject',
|
||||
'PhabricatorBotUser' => 'PhabricatorBotTarget',
|
||||
'PhabricatorBotWhatsNewHandler' => 'PhabricatorBotHandler',
|
||||
'PhabricatorBritishEnglishTranslation' => 'PhutilTranslation',
|
||||
'PhabricatorBuiltinDraftEngine' => 'PhabricatorDraftEngine',
|
||||
'PhabricatorBuiltinPatchList' => 'PhabricatorSQLPatchList',
|
||||
|
@ -7338,7 +7326,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorCalendarRemarkupRule' => 'PhabricatorObjectRemarkupRule',
|
||||
'PhabricatorCalendarReplyHandler' => 'PhabricatorApplicationTransactionReplyHandler',
|
||||
'PhabricatorCalendarSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorCampfireProtocolAdapter' => 'PhabricatorStreamingProtocolAdapter',
|
||||
'PhabricatorCelerityApplication' => 'PhabricatorApplication',
|
||||
'PhabricatorCelerityTestCase' => 'PhabricatorTestCase',
|
||||
'PhabricatorChangeParserTestCase' => 'PhabricatorWorkingCopyTestCase',
|
||||
|
@ -7499,6 +7486,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorConpherenceNotificationsSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorConpherencePreferencesSettingsPanel' => 'PhabricatorEditEngineSettingsPanel',
|
||||
'PhabricatorConpherenceProfileMenuItem' => 'PhabricatorProfileMenuItem',
|
||||
'PhabricatorConpherenceSoundSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorConpherenceThreadPHIDType' => 'PhabricatorPHIDType',
|
||||
'PhabricatorConpherenceWidgetVisibleSetting' => 'PhabricatorInternalSetting',
|
||||
'PhabricatorConsoleApplication' => 'PhabricatorApplication',
|
||||
|
@ -7906,6 +7894,8 @@ phutil_register_library_map(array(
|
|||
'PhabricatorPolicyInterface',
|
||||
'PhabricatorDestructibleInterface',
|
||||
'PhabricatorConduitResultInterface',
|
||||
'PhabricatorIndexableInterface',
|
||||
'PhabricatorNgramsInterface',
|
||||
),
|
||||
'PhabricatorFileAES256StorageFormat' => 'PhabricatorFileStorageFormat',
|
||||
'PhabricatorFileBundleLoader' => 'Phobject',
|
||||
|
@ -7924,6 +7914,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileDAO' => 'PhabricatorLiskDAO',
|
||||
'PhabricatorFileDataController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDeleteController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileDeleteTransaction' => 'PhabricatorFileTransactionType',
|
||||
'PhabricatorFileDropUploadController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileEditController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileEditEngine' => 'PhabricatorEditEngine',
|
||||
|
@ -7952,6 +7943,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFileLightboxController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileLinkView' => 'AphrontTagView',
|
||||
'PhabricatorFileListController' => 'PhabricatorFileController',
|
||||
'PhabricatorFileNameNgrams' => 'PhabricatorSearchNgrams',
|
||||
'PhabricatorFileNameTransaction' => 'PhabricatorFileTransactionType',
|
||||
'PhabricatorFileQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'PhabricatorFileROT13StorageFormat' => 'PhabricatorFileStorageFormat',
|
||||
|
@ -8026,6 +8018,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorFulltextEngineExtension' => 'Phobject',
|
||||
'PhabricatorFulltextEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorFulltextIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||
'PhabricatorFulltextInterface' => 'PhabricatorIndexableInterface',
|
||||
'PhabricatorFulltextResultSet' => 'Phobject',
|
||||
'PhabricatorFulltextStorageEngine' => 'Phobject',
|
||||
'PhabricatorFulltextToken' => 'Phobject',
|
||||
|
@ -8089,7 +8082,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorHovercardEngineExtensionModule' => 'PhabricatorConfigModule',
|
||||
'PhabricatorIDsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorIDsSearchField' => 'PhabricatorSearchField',
|
||||
'PhabricatorIRCProtocolAdapter' => 'PhabricatorProtocolAdapter',
|
||||
'PhabricatorIconDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorIconRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorIconSet' => 'Phobject',
|
||||
|
@ -8300,6 +8292,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorNavigationRemarkupRule' => 'PhutilRemarkupRule',
|
||||
'PhabricatorNeverTriggerClock' => 'PhabricatorTriggerClock',
|
||||
'PhabricatorNgramsIndexEngineExtension' => 'PhabricatorIndexEngineExtension',
|
||||
'PhabricatorNgramsInterface' => 'PhabricatorIndexableInterface',
|
||||
'PhabricatorNotificationBuilder' => 'Phobject',
|
||||
'PhabricatorNotificationClearController' => 'PhabricatorNotificationController',
|
||||
'PhabricatorNotificationClient' => 'Phobject',
|
||||
|
@ -8711,7 +8704,6 @@ phutil_register_library_map(array(
|
|||
),
|
||||
'PhabricatorPhurlURLAccessController' => 'PhabricatorPhurlController',
|
||||
'PhabricatorPhurlURLAliasTransaction' => 'PhabricatorPhurlURLTransactionType',
|
||||
'PhabricatorPhurlURLCommentController' => 'PhabricatorPhurlController',
|
||||
'PhabricatorPhurlURLCreateCapability' => 'PhabricatorPolicyCapability',
|
||||
'PhabricatorPhurlURLDatasource' => 'PhabricatorTypeaheadDatasource',
|
||||
'PhabricatorPhurlURLDescriptionTransaction' => 'PhabricatorPhurlURLTransactionType',
|
||||
|
@ -8947,7 +8939,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorProjectsSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
|
||||
'PhabricatorProjectsWatchersSearchEngineAttachment' => 'PhabricatorSearchEngineAttachment',
|
||||
'PhabricatorPronounSetting' => 'PhabricatorSelectSetting',
|
||||
'PhabricatorProtocolAdapter' => 'Phobject',
|
||||
'PhabricatorPygmentSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorQuery' => 'Phobject',
|
||||
'PhabricatorQueryConstraint' => 'Phobject',
|
||||
|
@ -9351,7 +9342,6 @@ phutil_register_library_map(array(
|
|||
'PhabricatorStoragePatch' => 'Phobject',
|
||||
'PhabricatorStorageSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||
'PhabricatorStorageSetupCheck' => 'PhabricatorSetupCheck',
|
||||
'PhabricatorStreamingProtocolAdapter' => 'PhabricatorProtocolAdapter',
|
||||
'PhabricatorStringListEditField' => 'PhabricatorEditField',
|
||||
'PhabricatorStringSetting' => 'PhabricatorSetting',
|
||||
'PhabricatorSubmitEditField' => 'PhabricatorEditField',
|
||||
|
|
|
@ -139,4 +139,29 @@ final class AphrontFileResponse extends AphrontResponse {
|
|||
return $this->getCompressResponse();
|
||||
}
|
||||
|
||||
public function parseHTTPRange($range) {
|
||||
$begin = null;
|
||||
$end = null;
|
||||
|
||||
$matches = null;
|
||||
if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) {
|
||||
// Note that the "Range" header specifies bytes differently than
|
||||
// we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
|
||||
$begin = (int)$matches[1];
|
||||
|
||||
// The "Range" may be "200-299" or "200-", meaning "until end of file".
|
||||
if (strlen($matches[2])) {
|
||||
$range_end = (int)$matches[2];
|
||||
$end = $range_end + 1;
|
||||
} else {
|
||||
$range_end = null;
|
||||
}
|
||||
|
||||
$this->setHTTPResponseCode(206);
|
||||
$this->setRange($begin, $range_end);
|
||||
}
|
||||
|
||||
return array($begin, $end);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,8 +9,10 @@
|
|||
* @task meta Application Management
|
||||
*/
|
||||
abstract class PhabricatorApplication
|
||||
extends Phobject
|
||||
implements PhabricatorPolicyInterface {
|
||||
extends PhabricatorLiskDAO
|
||||
implements
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorApplicationTransactionInterface {
|
||||
|
||||
const GROUP_CORE = 'core';
|
||||
const GROUP_UTILITIES = 'util';
|
||||
|
@ -26,6 +28,30 @@ abstract class PhabricatorApplication
|
|||
);
|
||||
}
|
||||
|
||||
final public function getApplicationName() {
|
||||
return 'application';
|
||||
}
|
||||
|
||||
final public function getTableName() {
|
||||
return 'application_application';
|
||||
}
|
||||
|
||||
final protected function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
final public function generatePHID() {
|
||||
return $this->getPHID();
|
||||
}
|
||||
|
||||
final public function save() {
|
||||
// When "save()" is called on applications, we just return without
|
||||
// actually writing anything to the database.
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( Application Information )-------------------------------------------- */
|
||||
|
||||
|
@ -613,4 +639,25 @@ abstract class PhabricatorApplication
|
|||
);
|
||||
}
|
||||
|
||||
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
|
||||
|
||||
|
||||
public function getApplicationTransactionEditor() {
|
||||
return new PhutilMethodNotImplementedException(pht('Coming Soon!'));
|
||||
}
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionTemplate() {
|
||||
return new PhabricatorApplicationApplicationTransaction();
|
||||
}
|
||||
|
||||
public function willRenderTimeline(
|
||||
PhabricatorApplicationTransactionView $timeline,
|
||||
AphrontRequest $request) {
|
||||
|
||||
return $timeline;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -104,9 +104,30 @@ abstract class CelerityResourceController extends PhabricatorController {
|
|||
}
|
||||
|
||||
$response = id(new AphrontFileResponse())
|
||||
->setMimeType($type_map[$type]);
|
||||
|
||||
$range = AphrontRequest::getHTTPHeader('Range');
|
||||
|
||||
if (strlen($range)) {
|
||||
$response->setContentLength(strlen($data));
|
||||
|
||||
list($range_begin, $range_end) = $response->parseHTTPRange($range);
|
||||
|
||||
if ($range_begin !== null) {
|
||||
if ($range_end !== null) {
|
||||
$data = substr($data, $range_begin, ($range_end - $range_begin));
|
||||
} else {
|
||||
$data = substr($data, $range_begin);
|
||||
}
|
||||
}
|
||||
|
||||
$response->setContentIterator(array($data));
|
||||
} else {
|
||||
$response
|
||||
->setContent($data)
|
||||
->setMimeType($type_map[$type])
|
||||
->setCompressResponse(true);
|
||||
}
|
||||
|
||||
|
||||
// NOTE: This is a piece of magic required to make WOFF fonts work in
|
||||
// Firefox and IE. Possibly we should generalize this more.
|
||||
|
|
|
@ -103,10 +103,20 @@ final class PhabricatorConfigClusterNotificationsController
|
|||
new PhutilNumber(idx($details, 'messages.in')),
|
||||
new PhutilNumber(idx($details, 'messages.out')));
|
||||
|
||||
if (idx($details, 'history.size')) {
|
||||
$history = pht(
|
||||
'%s Held / %sms',
|
||||
new PhutilNumber(idx($details, 'history.size')),
|
||||
new PhutilNumber(idx($details, 'history.age')));
|
||||
} else {
|
||||
$history = pht('No Messages');
|
||||
}
|
||||
|
||||
} else {
|
||||
$uptime = null;
|
||||
$clients = null;
|
||||
$stats = null;
|
||||
$history = null;
|
||||
}
|
||||
|
||||
$status_view = array(
|
||||
|
@ -126,6 +136,7 @@ final class PhabricatorConfigClusterNotificationsController
|
|||
$uptime,
|
||||
$clients,
|
||||
$stats,
|
||||
$history,
|
||||
$messages,
|
||||
);
|
||||
}
|
||||
|
@ -143,6 +154,7 @@ final class PhabricatorConfigClusterNotificationsController
|
|||
pht('Uptime'),
|
||||
pht('Clients'),
|
||||
pht('Messages'),
|
||||
pht('History'),
|
||||
null,
|
||||
))
|
||||
->setColumnClasses(
|
||||
|
@ -155,6 +167,7 @@ final class PhabricatorConfigClusterNotificationsController
|
|||
null,
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
'wide',
|
||||
));
|
||||
|
||||
|
|
|
@ -11,6 +11,13 @@ final class PhabricatorConfigDatabaseSchema
|
|||
public function addTable(PhabricatorConfigTableSchema $table) {
|
||||
$key = $table->getName();
|
||||
if (isset($this->tables[$key])) {
|
||||
|
||||
if ($key == 'application_application') {
|
||||
// NOTE: This is a terrible hack to allow Application subclasses to
|
||||
// extend LiskDAO so we can apply transactions to them.
|
||||
return $this;
|
||||
}
|
||||
|
||||
throw new Exception(
|
||||
pht('Trying to add duplicate table "%s"!', $key));
|
||||
}
|
||||
|
|
|
@ -107,7 +107,8 @@ final class ConpherenceRoomTestCase extends ConpherenceTestCase {
|
|||
|
||||
$xactions = array();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participant_phids));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
|
|
|
@ -9,7 +9,8 @@ abstract class ConpherenceTestCase extends PhabricatorTestCase {
|
|||
|
||||
$xactions = array(
|
||||
id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participant_phids)),
|
||||
);
|
||||
$editor = id(new ConpherenceEditor())
|
||||
|
@ -26,7 +27,8 @@ abstract class ConpherenceTestCase extends PhabricatorTestCase {
|
|||
|
||||
$xactions = array(
|
||||
id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('-' => $participant_phids)),
|
||||
);
|
||||
$editor = id(new ConpherenceEditor())
|
||||
|
|
|
@ -55,6 +55,8 @@ final class PhabricatorConpherenceApplication extends PhabricatorApplication {
|
|||
=> 'ConpherenceNotificationPanelController',
|
||||
'participant/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceParticipantController',
|
||||
'preferences/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceRoomPreferencesController',
|
||||
'update/(?P<id>[1-9]\d*)/'
|
||||
=> 'ConpherenceUpdateController',
|
||||
),
|
||||
|
|
|
@ -36,8 +36,7 @@ final class ConpherenceQueryThreadConduitAPIMethod
|
|||
$offset = $request->getValue('offset');
|
||||
|
||||
$query = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->needParticipantCache(true);
|
||||
->setViewer($user);
|
||||
|
||||
if ($ids) {
|
||||
$conpherences = $query
|
||||
|
@ -57,7 +56,7 @@ final class ConpherenceQueryThreadConduitAPIMethod
|
|||
->setLimit($limit)
|
||||
->setOffset($offset)
|
||||
->execute();
|
||||
$conpherence_phids = array_keys($participation);
|
||||
$conpherence_phids = mpull($participation, 'getConpherencePHID');
|
||||
$query->withPHIDs($conpherence_phids);
|
||||
$conpherences = $query->execute();
|
||||
$conpherences = array_select_keys($conpherences, $conpherence_phids);
|
||||
|
|
|
@ -69,7 +69,7 @@ final class ConpherenceUpdateThreadConduitAPIMethod
|
|||
if ($add_participant_phids) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $add_participant_phids));
|
||||
}
|
||||
if ($remove_participant_phid) {
|
||||
|
@ -78,7 +78,7 @@ final class ConpherenceUpdateThreadConduitAPIMethod
|
|||
}
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('-' => array($remove_participant_phid)));
|
||||
}
|
||||
if ($title) {
|
||||
|
|
|
@ -1,8 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceParticipationStatus extends ConpherenceConstants {
|
||||
|
||||
const UP_TO_DATE = 0;
|
||||
const BEHIND = 1;
|
||||
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceRoomSettings extends ConpherenceConstants {
|
||||
|
||||
const SOUND_RECEIVE = 'receive';
|
||||
const SOUND_MENTION = 'mention';
|
||||
|
||||
const DEFAULT_RECEIVE_SOUND = 'tap';
|
||||
const DEFAULT_MENTION_SOUND = 'alert';
|
||||
const DEFAULT_NO_SOUND = 'none';
|
||||
|
||||
const COLOR_LIGHT = 'light';
|
||||
const COLOR_BLUE = 'blue';
|
||||
const COLOR_INDIGO = 'indigo';
|
||||
const COLOR_PEACH = 'peach';
|
||||
const COLOR_GREEN = 'green';
|
||||
const COLOR_PINK = 'pink';
|
||||
|
||||
public static function getSoundMap() {
|
||||
return array(
|
||||
'none' => array(
|
||||
'name' => pht('No Sound'),
|
||||
'rsrc' => '',
|
||||
),
|
||||
'alert' => array(
|
||||
'name' => pht('Alert'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/alert.mp3'),
|
||||
),
|
||||
'bing' => array(
|
||||
'name' => pht('Bing'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/bing.mp3'),
|
||||
),
|
||||
'pock' => array(
|
||||
'name' => pht('Pock'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/pock.mp3'),
|
||||
),
|
||||
'tap' => array(
|
||||
'name' => pht('Tap'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/tap.mp3'),
|
||||
),
|
||||
'ting' => array(
|
||||
'name' => pht('Ting'),
|
||||
'rsrc' => celerity_get_resource_uri('/rsrc/audio/basic/ting.mp3'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getDropdownSoundMap() {
|
||||
$map = self::getSoundMap();
|
||||
return ipull($map, 'name');
|
||||
}
|
||||
|
||||
public static function getThemeMap() {
|
||||
return array(
|
||||
self::COLOR_LIGHT => pht('Light'),
|
||||
self::COLOR_BLUE => pht('Blue'),
|
||||
self::COLOR_INDIGO => pht('Indigo'),
|
||||
self::COLOR_PEACH => pht('Peach'),
|
||||
self::COLOR_GREEN => pht('Green'),
|
||||
self::COLOR_PINK => pht('Pink'),
|
||||
);
|
||||
}
|
||||
|
||||
public static function getThemeClass($theme) {
|
||||
return 'conpherence-theme-'.$theme;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -8,7 +8,6 @@ final class ConpherenceUpdateActions extends ConpherenceConstants {
|
|||
const JOIN_ROOM = 'join_room';
|
||||
const ADD_PERSON = 'add_person';
|
||||
const REMOVE_PERSON = 'remove_person';
|
||||
const NOTIFICATIONS = 'notifications';
|
||||
const ADD_STATUS = 'add_status';
|
||||
const LOAD = 'load';
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ final class ConpherenceColumnViewController extends
|
|||
$transactions = $conpherence->getTransactions();
|
||||
$latest_transaction = head($transactions);
|
||||
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$participant->markUpToDate($conpherence, $latest_transaction);
|
||||
$participant->markUpToDate($conpherence);
|
||||
unset($write_guard);
|
||||
|
||||
$draft = PhabricatorDraft::newFromUserAndKey(
|
||||
|
|
|
@ -54,19 +54,29 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
}
|
||||
|
||||
protected function buildHeaderPaneContent(
|
||||
ConpherenceThread $conpherence,
|
||||
array $policy_objects) {
|
||||
assert_instances_of($policy_objects, 'PhabricatorPolicy');
|
||||
ConpherenceThread $conpherence) {
|
||||
$viewer = $this->getViewer();
|
||||
$header = null;
|
||||
$id = $conpherence->getID();
|
||||
|
||||
if ($conpherence->getID()) {
|
||||
if ($id) {
|
||||
$data = $conpherence->getDisplayData($this->getViewer());
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setViewer($viewer)
|
||||
->setHeader($data['title'])
|
||||
->setSubheader($data['topic'])
|
||||
->setPolicyObject($conpherence)
|
||||
->setImage($data['image']);
|
||||
|
||||
if (strlen($data['topic'])) {
|
||||
$topic = id(new PHUITagView())
|
||||
->setName($data['topic'])
|
||||
->setShade(PHUITagView::COLOR_VIOLET)
|
||||
->setType(PHUITagView::TYPE_SHADE)
|
||||
->addClass('conpherence-header-topic');
|
||||
$header->addTag($topic);
|
||||
}
|
||||
|
||||
$can_edit = PhabricatorPolicyFilter::hasCapability(
|
||||
$viewer,
|
||||
$conpherence,
|
||||
|
@ -74,15 +84,14 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
|
||||
if ($can_edit) {
|
||||
$header->setImageURL(
|
||||
$this->getApplicationURI('picture/'.$conpherence->getID().'/'));
|
||||
$this->getApplicationURI("picture/{$id}/"));
|
||||
}
|
||||
|
||||
$participating = $conpherence->getParticipantIfExists($viewer->getPHID());
|
||||
|
||||
$header->addActionItem(
|
||||
id(new PHUIIconCircleView())
|
||||
->setHref(
|
||||
$this->getApplicationURI('update/'.$conpherence->getID()).'/')
|
||||
->setHref($this->getApplicationURI("update/{$id}/"))
|
||||
->setIcon('fa-pencil')
|
||||
->addClass('hide-on-device')
|
||||
->setColor('violet')
|
||||
|
@ -90,9 +99,7 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
|
||||
$header->addActionItem(
|
||||
id(new PHUIIconCircleView())
|
||||
->setHref(
|
||||
$this->getApplicationURI('update/'.$conpherence->getID()).'/'.
|
||||
'?action='.ConpherenceUpdateActions::NOTIFICATIONS)
|
||||
->setHref($this->getApplicationURI("preferences/{$id}/"))
|
||||
->setIcon('fa-gear')
|
||||
->addClass('hide-on-device')
|
||||
->setColor('pink')
|
||||
|
@ -127,7 +134,7 @@ abstract class ConpherenceController extends PhabricatorController {
|
|||
|
||||
if (!$participating) {
|
||||
$action = ConpherenceUpdateActions::JOIN_ROOM;
|
||||
$uri = $this->getApplicationURI('update/'.$conpherence->getID().'/');
|
||||
$uri = $this->getApplicationURI("update/{$id}/");
|
||||
$button = phutil_tag(
|
||||
'button',
|
||||
array(
|
||||
|
|
|
@ -34,7 +34,7 @@ final class ConpherenceListController extends ConpherenceController {
|
|||
$title = pht('Conpherence');
|
||||
$conpherence = null;
|
||||
|
||||
$limit = (ConpherenceThreadListView::SEE_MORE_LIMIT * 2) + 1;
|
||||
$limit = ConpherenceThreadListView::SEE_ALL_LIMIT + 1;
|
||||
$all_participation = array();
|
||||
|
||||
$mode = $this->determineMode();
|
||||
|
@ -64,7 +64,7 @@ final class ConpherenceListController extends ConpherenceController {
|
|||
}
|
||||
|
||||
// check to see if the loaded conpherence is going to show up
|
||||
// within the SEE_MORE_LIMIT amount of conpherences.
|
||||
// within the SEE_ALL_LIMIT amount of conpherences.
|
||||
// If its not there, then we just pre-pend it as the "first"
|
||||
// conpherence so folks have a navigation item in the menu.
|
||||
$count = 0;
|
||||
|
@ -75,7 +75,7 @@ final class ConpherenceListController extends ConpherenceController {
|
|||
break;
|
||||
}
|
||||
$count++;
|
||||
if ($count > ConpherenceThreadListView::SEE_MORE_LIMIT) {
|
||||
if ($count > ConpherenceThreadListView::SEE_ALL_LIMIT) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
@ -89,11 +89,19 @@ final class ConpherenceListController extends ConpherenceController {
|
|||
default:
|
||||
$data = $this->loadDefaultParticipation($limit);
|
||||
$all_participation = $data['all_participation'];
|
||||
if ($all_participation) {
|
||||
$conpherence_id = head($all_participation)->getConpherencePHID();
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->withPHIDs(array($conpherence_id))
|
||||
->needProfileImage(true)
|
||||
->executeOne();
|
||||
}
|
||||
// If $conpherence is null, NUX state will render
|
||||
break;
|
||||
}
|
||||
|
||||
$threads = $this->loadConpherenceThreadData(
|
||||
$all_participation);
|
||||
$threads = $this->loadConpherenceThreadData($all_participation);
|
||||
|
||||
$thread_view = id(new ConpherenceThreadListView())
|
||||
->setUser($user)
|
||||
|
@ -144,6 +152,7 @@ final class ConpherenceListController extends ConpherenceController {
|
|||
->withParticipantPHIDs(array($viewer->getPHID()))
|
||||
->setLimit($limit)
|
||||
->execute();
|
||||
$all_participation = mpull($all_participation, null, 'getConpherencePHID');
|
||||
|
||||
return array(
|
||||
'all_participation' => $all_participation,
|
||||
|
|
|
@ -23,7 +23,8 @@ final class ConpherenceNewRoomController extends ConpherenceController {
|
|||
$participants[] = $user->getPHID();
|
||||
$participants = array_unique($participants);
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participants));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceThreadTopicTransaction::TRANSACTIONTYPE)
|
||||
|
|
|
@ -7,12 +7,12 @@ final class ConpherenceNotificationPanelController
|
|||
$user = $request->getUser();
|
||||
$conpherences = array();
|
||||
require_celerity_resource('conpherence-notification-css');
|
||||
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
||||
|
||||
$participant_data = id(new ConpherenceParticipantQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()))
|
||||
->setLimit(5)
|
||||
->execute();
|
||||
$participant_data = mpull($participant_data, null, 'getConpherencePHID');
|
||||
|
||||
if ($participant_data) {
|
||||
$conpherences = id(new ConpherenceThreadQuery())
|
||||
|
@ -37,7 +37,7 @@ final class ConpherenceNotificationPanelController
|
|||
'conpherence-notification',
|
||||
);
|
||||
|
||||
if ($p_data->getParticipationStatus() == $unread_status) {
|
||||
if (!$p_data->isUpToDate($conpherence)) {
|
||||
$classes[] = 'phabricator-notification-unread';
|
||||
}
|
||||
$uri = $this->getApplicationURI($conpherence->getID().'/');
|
||||
|
@ -95,7 +95,7 @@ final class ConpherenceNotificationPanelController
|
|||
|
||||
$unread = id(new ConpherenceParticipantCountQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()))
|
||||
->withParticipationStatus($unread_status)
|
||||
->withUnread(true)
|
||||
->execute();
|
||||
$unread_count = idx($unread, $user->getPHID(), 0);
|
||||
|
||||
|
|
|
@ -0,0 +1,113 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceRoomPreferencesController
|
||||
extends ConpherenceController {
|
||||
|
||||
public function shouldAllowPublic() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
$viewer = $request->getViewer();
|
||||
$conpherence_id = $request->getURIData('id');
|
||||
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($conpherence_id))
|
||||
->executeOne();
|
||||
if (!$conpherence) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$view_uri = $conpherence->getURI();
|
||||
|
||||
$participant = $conpherence->getParticipantIfExists($viewer->getPHID());
|
||||
if (!$participant) {
|
||||
if ($viewer->isLoggedIn()) {
|
||||
$text = pht(
|
||||
'Notification settings are available after joining the room.');
|
||||
} else {
|
||||
$text = pht(
|
||||
'Notification settings are available after logging in and joining '.
|
||||
'the room.');
|
||||
}
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->addCancelButton($view_uri)
|
||||
->appendParagraph($text);
|
||||
}
|
||||
|
||||
// Save the data and redirect
|
||||
if ($request->isFormPost()) {
|
||||
$notifications = $request->getStr('notifications');
|
||||
$sounds = $request->getArr('sounds');
|
||||
$theme = $request->getStr('theme');
|
||||
|
||||
$participant->setSettings(array(
|
||||
'notifications' => $notifications,
|
||||
'sounds' => $sounds,
|
||||
'theme' => $theme,
|
||||
));
|
||||
$participant->save();
|
||||
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($view_uri);
|
||||
}
|
||||
|
||||
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
|
||||
$notification_default = $viewer->getUserSetting($notification_key);
|
||||
|
||||
$sound_key = PhabricatorConpherenceSoundSetting::SETTINGKEY;
|
||||
$sound_default = $viewer->getUserSetting($sound_key);
|
||||
|
||||
$settings = $participant->getSettings();
|
||||
$notifications = idx($settings, 'notifications', $notification_default);
|
||||
$theme = idx($settings, 'theme', ConpherenceRoomSettings::COLOR_LIGHT);
|
||||
|
||||
$sounds = idx($settings, 'sounds', array());
|
||||
$map = PhabricatorConpherenceSoundSetting::getDefaultSound($sound_default);
|
||||
$receive = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE,
|
||||
$map[ConpherenceRoomSettings::SOUND_RECEIVE]);
|
||||
$mention = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_MENTION,
|
||||
$map[ConpherenceRoomSettings::SOUND_MENTION]);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($viewer)
|
||||
->appendControl(
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->setLabel(pht('Notify'))
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL),
|
||||
'')
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY),
|
||||
'')
|
||||
->setName('notifications')
|
||||
->setValue($notifications))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('New Message'))
|
||||
->setName('sounds['.ConpherenceRoomSettings::SOUND_RECEIVE.']')
|
||||
->setOptions(ConpherenceRoomSettings::getDropdownSoundMap())
|
||||
->setValue($receive))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel(pht('Theme'))
|
||||
->setName('theme')
|
||||
->setOptions(ConpherenceRoomSettings::getThemeMap())
|
||||
->setValue($theme));
|
||||
|
||||
return $this->newDialog()
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->appendForm($form)
|
||||
->addCancelButton($view_uri)
|
||||
->addSubmitButton(pht('Save'));
|
||||
}
|
||||
|
||||
}
|
|
@ -24,9 +24,6 @@ final class ConpherenceUpdateController
|
|||
case ConpherenceUpdateActions::METADATA:
|
||||
$needed_capabilities[] = PhabricatorPolicyCapability::CAN_EDIT;
|
||||
break;
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
$need_participants = true;
|
||||
break;
|
||||
case ConpherenceUpdateActions::LOAD:
|
||||
break;
|
||||
}
|
||||
|
@ -61,7 +58,7 @@ final class ConpherenceUpdateController
|
|||
case ConpherenceUpdateActions::JOIN_ROOM:
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => array($user->getPHID())));
|
||||
$delete_draft = true;
|
||||
$message = $request->getStr('text');
|
||||
|
@ -95,7 +92,7 @@ final class ConpherenceUpdateController
|
|||
if (!empty($person_phids)) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $person_phids));
|
||||
}
|
||||
break;
|
||||
|
@ -108,22 +105,10 @@ final class ConpherenceUpdateController
|
|||
if ($person_phid) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(
|
||||
ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('-' => array($person_phid)));
|
||||
$response_mode = 'go-home';
|
||||
}
|
||||
break;
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
$notifications = $request->getStr('notifications');
|
||||
$participant = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
if (!$participant) {
|
||||
return id(new Aphront404Response());
|
||||
}
|
||||
$participant->setSettings(array('notifications' => $notifications));
|
||||
$participant->save();
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI('/'.$conpherence->getMonogram());
|
||||
|
||||
break;
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
$title = $request->getStr('title');
|
||||
|
@ -217,9 +202,6 @@ final class ConpherenceUpdateController
|
|||
}
|
||||
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
$dialog = $this->renderPreferencesDialog($conpherence);
|
||||
break;
|
||||
case ConpherenceUpdateActions::ADD_PERSON:
|
||||
$dialog = $this->renderAddPersonDialog($conpherence);
|
||||
break;
|
||||
|
@ -242,64 +224,6 @@ final class ConpherenceUpdateController
|
|||
|
||||
}
|
||||
|
||||
private function renderPreferencesDialog(
|
||||
ConpherenceThread $conpherence) {
|
||||
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$participant = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
if (!$participant) {
|
||||
if ($user->isLoggedIn()) {
|
||||
$text = pht(
|
||||
'Notification settings are available after joining the room.');
|
||||
} else {
|
||||
$text = pht(
|
||||
'Notification settings are available after logging in and joining '.
|
||||
'the room.');
|
||||
}
|
||||
return id(new AphrontDialogView())
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->appendParagraph($text);
|
||||
}
|
||||
|
||||
$notification_key = PhabricatorConpherenceNotificationsSetting::SETTINGKEY;
|
||||
$notification_default = $user->getUserSetting($notification_key);
|
||||
|
||||
$settings = $participant->getSettings();
|
||||
$notifications = idx(
|
||||
$settings,
|
||||
'notifications',
|
||||
$notification_default);
|
||||
|
||||
$form = id(new AphrontFormView())
|
||||
->setUser($user)
|
||||
->setFullWidth(true)
|
||||
->appendControl(
|
||||
id(new AphrontFormRadioButtonControl())
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_EMAIL),
|
||||
'')
|
||||
->addButton(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY,
|
||||
PhabricatorConpherenceNotificationsSetting::getSettingLabel(
|
||||
PhabricatorConpherenceNotificationsSetting::VALUE_CONPHERENCE_NOTIFY),
|
||||
'')
|
||||
->setName('notifications')
|
||||
->setValue($notifications));
|
||||
|
||||
return id(new AphrontDialogView())
|
||||
->setTitle(pht('Room Preferences'))
|
||||
->addHiddenInput('action', 'notifications')
|
||||
->addHiddenInput(
|
||||
'latest_transaction_id',
|
||||
$request->getInt('latest_transaction_id'))
|
||||
->appendForm($form);
|
||||
|
||||
}
|
||||
|
||||
private function renderAddPersonDialog(
|
||||
ConpherenceThread $conpherence) {
|
||||
|
||||
|
@ -360,9 +284,9 @@ final class ConpherenceUpdateController
|
|||
$body[] = pht(
|
||||
'Are you sure you want to leave this room?');
|
||||
} else {
|
||||
$title = pht('Banish User');
|
||||
$title = pht('Remove Participant');
|
||||
$body[] = pht(
|
||||
'Banish %s from the realm?',
|
||||
'Remove %s from this room?',
|
||||
phutil_tag('strong', array(), $removed_user->getUsername()));
|
||||
}
|
||||
|
||||
|
@ -372,7 +296,7 @@ final class ConpherenceUpdateController
|
|||
'You will be able to rejoin the room later.');
|
||||
} else {
|
||||
$body[] = pht(
|
||||
'This user will be able to rejoin the room later.');
|
||||
'They will be able to rejoin the room later.');
|
||||
}
|
||||
} else {
|
||||
if ($is_self) {
|
||||
|
@ -387,7 +311,7 @@ final class ConpherenceUpdateController
|
|||
}
|
||||
} else {
|
||||
$body[] = pht(
|
||||
'This user will not be able to rejoin the room unless invited '.
|
||||
'They will not be able to rejoin the room unless invited '.
|
||||
'again.');
|
||||
}
|
||||
}
|
||||
|
@ -480,7 +404,6 @@ final class ConpherenceUpdateController
|
|||
$need_transactions = true;
|
||||
break;
|
||||
case ConpherenceUpdateActions::REMOVE_PERSON:
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
default:
|
||||
break;
|
||||
|
||||
|
@ -496,6 +419,8 @@ final class ConpherenceUpdateController
|
|||
->executeOne();
|
||||
|
||||
$non_update = false;
|
||||
$participant = $conpherence->getParticipant($user->getPHID());
|
||||
|
||||
if ($need_transactions && $conpherence->getTransactions()) {
|
||||
$data = ConpherenceTransactionRenderer::renderTransactions(
|
||||
$user,
|
||||
|
@ -503,9 +428,7 @@ final class ConpherenceUpdateController
|
|||
$key = PhabricatorConpherenceColumnMinimizeSetting::SETTINGKEY;
|
||||
$minimized = $user->getUserSetting($key);
|
||||
if (!$minimized) {
|
||||
$participant_obj = $conpherence->getParticipant($user->getPHID());
|
||||
$participant_obj
|
||||
->markUpToDate($conpherence, $data['latest_transaction']);
|
||||
$participant->markUpToDate($conpherence);
|
||||
}
|
||||
} else if ($need_transactions) {
|
||||
$non_update = true;
|
||||
|
@ -522,18 +445,12 @@ final class ConpherenceUpdateController
|
|||
$people_widget = null;
|
||||
switch ($action) {
|
||||
case ConpherenceUpdateActions::METADATA:
|
||||
$policy_objects = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($user)
|
||||
->setObject($conpherence)
|
||||
->execute();
|
||||
$header = $this->buildHeaderPaneContent(
|
||||
$conpherence,
|
||||
$policy_objects);
|
||||
$header = $this->buildHeaderPaneContent($conpherence);
|
||||
$header = hsprintf('%s', $header);
|
||||
$nav_item = id(new ConpherenceThreadListView())
|
||||
->setUser($user)
|
||||
->setBaseURI($this->getApplicationURI())
|
||||
->renderSingleThread($conpherence, $policy_objects);
|
||||
->renderThreadItem($conpherence);
|
||||
$nav_item = hsprintf('%s', $nav_item);
|
||||
break;
|
||||
case ConpherenceUpdateActions::ADD_PERSON:
|
||||
|
@ -544,7 +461,6 @@ final class ConpherenceUpdateController
|
|||
$people_widget = hsprintf('%s', $people_widget->render());
|
||||
break;
|
||||
case ConpherenceUpdateActions::REMOVE_PERSON:
|
||||
case ConpherenceUpdateActions::NOTIFICATIONS:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
@ -552,6 +468,11 @@ final class ConpherenceUpdateController
|
|||
$dropdown_query = id(new AphlictDropdownDataQuery())
|
||||
->setViewer($user);
|
||||
$dropdown_query->execute();
|
||||
|
||||
$sounds = $this->getSoundForParticipant($user, $participant);
|
||||
$receive_sound = $sounds[ConpherenceRoomSettings::SOUND_RECEIVE];
|
||||
$mention_sound = $sounds[ConpherenceRoomSettings::SOUND_MENTION];
|
||||
|
||||
$content = array(
|
||||
'non_update' => $non_update,
|
||||
'transactions' => hsprintf('%s', $rendered_transactions),
|
||||
|
@ -565,9 +486,40 @@ final class ConpherenceUpdateController
|
|||
$dropdown_query->getNotificationData(),
|
||||
$dropdown_query->getConpherenceData(),
|
||||
),
|
||||
'sound' => array(
|
||||
'receive' => $receive_sound,
|
||||
'mention' => $mention_sound,
|
||||
),
|
||||
);
|
||||
|
||||
return $content;
|
||||
}
|
||||
|
||||
protected function getSoundForParticipant(
|
||||
PhabricatorUser $user,
|
||||
ConpherenceParticipant $participant) {
|
||||
|
||||
$sound_key = PhabricatorConpherenceSoundSetting::SETTINGKEY;
|
||||
$sound_default = $user->getUserSetting($sound_key);
|
||||
|
||||
$settings = $participant->getSettings();
|
||||
$sounds = idx($settings, 'sounds', array());
|
||||
$map = PhabricatorConpherenceSoundSetting::getDefaultSound($sound_default);
|
||||
|
||||
$receive = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE,
|
||||
$map[ConpherenceRoomSettings::SOUND_RECEIVE]);
|
||||
$mention = idx($sounds,
|
||||
ConpherenceRoomSettings::SOUND_MENTION,
|
||||
$map[ConpherenceRoomSettings::SOUND_MENTION]);
|
||||
|
||||
$sound_map = ConpherenceRoomSettings::getSoundMap();
|
||||
|
||||
return array(
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE => $sound_map[$receive]['rsrc'],
|
||||
ConpherenceRoomSettings::SOUND_MENTION => $sound_map[$mention]['rsrc'],
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -55,15 +55,15 @@ final class ConpherenceViewController extends
|
|||
}
|
||||
$this->setConpherence($conpherence);
|
||||
|
||||
$transactions = $this->getNeededTransactions(
|
||||
$conpherence,
|
||||
$old_message_id);
|
||||
$latest_transaction = head($transactions);
|
||||
$participant = $conpherence->getParticipantIfExists($user->getPHID());
|
||||
$theme = ConpherenceRoomSettings::COLOR_LIGHT;
|
||||
|
||||
if ($participant) {
|
||||
$settings = $participant->getSettings();
|
||||
$theme = idx($settings, 'theme', ConpherenceRoomSettings::COLOR_LIGHT);
|
||||
if (!$participant->isUpToDate($conpherence)) {
|
||||
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$participant->markUpToDate($conpherence, $latest_transaction);
|
||||
$participant->markUpToDate($conpherence);
|
||||
$user->clearCacheData(PhabricatorUserMessageCountCacheType::KEY_COUNT);
|
||||
unset($write_guard);
|
||||
}
|
||||
|
@ -82,11 +82,7 @@ final class ConpherenceViewController extends
|
|||
$form = null;
|
||||
$content = array('transactions' => $messages);
|
||||
} else {
|
||||
$policy_objects = id(new PhabricatorPolicyQuery())
|
||||
->setViewer($user)
|
||||
->setObject($conpherence)
|
||||
->execute();
|
||||
$header = $this->buildHeaderPaneContent($conpherence, $policy_objects);
|
||||
$header = $this->buildHeaderPaneContent($conpherence);
|
||||
$search = $this->buildSearchForm();
|
||||
$form = $this->renderFormContent();
|
||||
$content = array(
|
||||
|
@ -126,6 +122,7 @@ final class ConpherenceViewController extends
|
|||
->setSearch($search)
|
||||
->setMessages($messages)
|
||||
->setReplyForm($form)
|
||||
->setTheme($theme)
|
||||
->setLatestTransactionID($data['latest_transaction_id'])
|
||||
->setRole('thread');
|
||||
|
||||
|
@ -203,33 +200,6 @@ final class ConpherenceViewController extends
|
|||
}
|
||||
}
|
||||
|
||||
private function getNeededTransactions(
|
||||
ConpherenceThread $conpherence,
|
||||
$message_id) {
|
||||
|
||||
if ($message_id) {
|
||||
$newer_transactions = $conpherence->getTransactions();
|
||||
$query = id(new ConpherenceTransactionQuery())
|
||||
->setViewer($this->getRequest()->getUser())
|
||||
->withObjectPHIDs(array($conpherence->getPHID()))
|
||||
->setAfterID($message_id)
|
||||
->needHandles(true)
|
||||
->setLimit(self::OLDER_FETCH_LIMIT);
|
||||
$older_transactions = $query->execute();
|
||||
$handles = array();
|
||||
foreach ($older_transactions as $transaction) {
|
||||
$handles += $transaction->getHandles();
|
||||
}
|
||||
$conpherence->attachHandles($conpherence->getHandles() + $handles);
|
||||
$transactions = array_merge($newer_transactions, $older_transactions);
|
||||
$conpherence->attachTransactions($transactions);
|
||||
} else {
|
||||
$transactions = $conpherence->getTransactions();
|
||||
}
|
||||
|
||||
return $transactions;
|
||||
}
|
||||
|
||||
private function getMainQueryLimit() {
|
||||
$request = $this->getRequest();
|
||||
$base_limit = ConpherenceThreadQuery::TRANSACTION_LIMIT;
|
||||
|
|
|
@ -37,7 +37,8 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
if (!$errors) {
|
||||
$xactions = array();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $participant_phids));
|
||||
if ($title) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
|
@ -87,8 +88,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = ConpherenceTransaction::TYPE_PARTICIPANTS;
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||
$types[] = PhabricatorTransactions::TYPE_VIEW_POLICY;
|
||||
$types[] = PhabricatorTransactions::TYPE_EDIT_POLICY;
|
||||
|
@ -100,29 +99,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
return pht('%s created this room.', $author);
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
if ($this->getIsNewObject()) {
|
||||
return array();
|
||||
}
|
||||
return $object->getParticipantPHIDs();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
return $this->getPHIDTransactionNewValue($xaction);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* We really only need a read lock if we have a comment. In that case, we
|
||||
* must update the messagesCount field on the conpherence and
|
||||
|
@ -142,72 +118,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
return $lock;
|
||||
}
|
||||
|
||||
/**
|
||||
* We need to apply initial effects IFF the conpherence is new. We must
|
||||
* save the conpherence first thing to make sure we have an id and a phid, as
|
||||
* well as create the initial set of participants so that we pass policy
|
||||
* checks.
|
||||
*/
|
||||
protected function shouldApplyInitialEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
return $this->getIsNewObject();
|
||||
}
|
||||
|
||||
protected function applyInitialEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$object->save();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
// Since this is a new ConpherenceThread, we have to create the
|
||||
// participation data asap to pass policy checks. For existing
|
||||
// ConpherenceThreads, the existing participation is correct
|
||||
// at this stage. Note that later in applyCustomExternalTransaction
|
||||
// this participation data will be updated, particularly the
|
||||
// behindTransactionPHID which is just a generated dummy for now.
|
||||
$participants = array();
|
||||
$phids = $this->getPHIDTransactionNewValue($xaction, array());
|
||||
foreach ($phids as $phid) {
|
||||
if ($phid == $this->getActor()->getPHID()) {
|
||||
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$message_count = 1;
|
||||
} else {
|
||||
$status = ConpherenceParticipationStatus::BEHIND;
|
||||
$message_count = 0;
|
||||
}
|
||||
$participants[$phid] =
|
||||
id(new ConpherenceParticipant())
|
||||
->setConpherencePHID($object->getPHID())
|
||||
->setParticipantPHID($phid)
|
||||
->setParticipationStatus($status)
|
||||
->setDateTouched(time())
|
||||
->setBehindTransactionPHID($xaction->generatePHID())
|
||||
->setSeenMessageCount($message_count)
|
||||
->save();
|
||||
$object->attachParticipants($participants);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
if (!$this->getIsNewObject()) {}
|
||||
break;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
protected function applyBuiltinInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
@ -221,96 +131,23 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
return parent::applyBuiltinInternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
if ($this->getIsNewObject()) {
|
||||
continue;
|
||||
}
|
||||
$participants = $object->getParticipants();
|
||||
|
||||
$old_map = array_fuse($xaction->getOldValue());
|
||||
$new_map = array_fuse($xaction->getNewValue());
|
||||
|
||||
$remove = array_keys(array_diff_key($old_map, $new_map));
|
||||
foreach ($remove as $phid) {
|
||||
$remove_participant = $participants[$phid];
|
||||
$remove_participant->delete();
|
||||
unset($participants[$phid]);
|
||||
}
|
||||
|
||||
$add = array_keys(array_diff_key($new_map, $old_map));
|
||||
foreach ($add as $phid) {
|
||||
if ($phid == $this->getActor()->getPHID()) {
|
||||
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$message_count = $object->getMessageCount();
|
||||
} else {
|
||||
$status = ConpherenceParticipationStatus::BEHIND;
|
||||
$message_count = 0;
|
||||
}
|
||||
$participants[$phid] =
|
||||
id(new ConpherenceParticipant())
|
||||
->setConpherencePHID($object->getPHID())
|
||||
->setParticipantPHID($phid)
|
||||
->setParticipationStatus($status)
|
||||
->setDateTouched(time())
|
||||
->setBehindTransactionPHID($xaction->getPHID())
|
||||
->setSeenMessageCount($message_count)
|
||||
->save();
|
||||
}
|
||||
$object->attachParticipants($participants);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyFinalEffects(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
if (!$xactions) {
|
||||
return $xactions;
|
||||
}
|
||||
|
||||
$message_count = 0;
|
||||
foreach ($xactions as $xaction) {
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
$message_count++;
|
||||
|
||||
// update everyone's participation status on a message -only-
|
||||
$xaction_phid = $xaction->getPHID();
|
||||
$behind = ConpherenceParticipationStatus::BEHIND;
|
||||
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$acting_phid = $this->getActingAsPHID();
|
||||
$participants = $object->getParticipants();
|
||||
$user = $this->getActor();
|
||||
$time = time();
|
||||
foreach ($participants as $phid => $participant) {
|
||||
if ($phid != $user->getPHID()) {
|
||||
if ($participant->getParticipationStatus() != $behind) {
|
||||
$participant->setBehindTransactionPHID($xaction_phid);
|
||||
$participant->setSeenMessageCount(
|
||||
$object->getMessageCount() - $message_count);
|
||||
foreach ($participants as $participant) {
|
||||
if ($participant->getParticipantPHID() == $acting_phid) {
|
||||
$participant->markUpToDate($object);
|
||||
}
|
||||
$participant->setParticipationStatus($behind);
|
||||
$participant->setDateTouched($time);
|
||||
} else {
|
||||
$participant->setSeenMessageCount($object->getMessageCount());
|
||||
$participant->setBehindTransactionPHID($xaction_phid);
|
||||
$participant->setParticipationStatus($up_to_date);
|
||||
$participant->setDateTouched($time);
|
||||
}
|
||||
$participant->save();
|
||||
}
|
||||
|
||||
if ($participants) {
|
||||
PhabricatorUserCache::clearCaches(
|
||||
PhabricatorUserMessageCountCacheType::KEY_COUNT,
|
||||
array_keys($participants));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($xactions) {
|
||||
|
@ -334,7 +171,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
parent::requireCapabilities($object, $xaction);
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
case ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE:
|
||||
$old_map = array_fuse($xaction->getOldValue());
|
||||
$new_map = array_fuse($xaction->getNewValue());
|
||||
|
||||
|
@ -359,6 +196,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
PhabricatorPolicyCapability::CAN_EDIT);
|
||||
}
|
||||
break;
|
||||
|
||||
case ConpherenceThreadTitleTransaction::TRANSACTIONTYPE:
|
||||
case ConpherenceThreadTopicTransaction::TRANSACTIONTYPE:
|
||||
PhabricatorPolicyFilter::requireCapability(
|
||||
|
@ -369,19 +207,6 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
}
|
||||
}
|
||||
|
||||
protected function mergeTransactions(
|
||||
PhabricatorApplicationTransaction $u,
|
||||
PhabricatorApplicationTransaction $v) {
|
||||
|
||||
$type = $u->getTransactionType();
|
||||
switch ($type) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||
}
|
||||
|
||||
return parent::mergeTransactions($u, $v);
|
||||
}
|
||||
|
||||
protected function shouldSendMail(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
@ -484,43 +309,4 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
return true;
|
||||
}
|
||||
|
||||
protected function validateTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
$type,
|
||||
array $xactions) {
|
||||
|
||||
$errors = parent::validateTransaction($object, $type, $xactions);
|
||||
|
||||
switch ($type) {
|
||||
case ConpherenceTransaction::TYPE_PARTICIPANTS:
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_phids = $this->getPHIDTransactionNewValue($xaction, array());
|
||||
$old_phids = nonempty($object->getParticipantPHIDs(), array());
|
||||
$phids = array_diff($new_phids, $old_phids);
|
||||
|
||||
if (!$phids) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$users = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($this->requireActor())
|
||||
->withPHIDs($phids)
|
||||
->execute();
|
||||
$users = mpull($users, null, 'getPHID');
|
||||
foreach ($phids as $phid) {
|
||||
if (isset($users[$phid])) {
|
||||
continue;
|
||||
}
|
||||
$errors[] = new PhabricatorApplicationTransactionValidationError(
|
||||
$type,
|
||||
pht('Invalid'),
|
||||
pht('New room participant "%s" is not a valid user.', $phid),
|
||||
$xaction);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -55,7 +55,8 @@ final class ConpherenceReplyHandler extends PhabricatorMailReplyHandler {
|
|||
$xactions = array();
|
||||
if ($this->getMailAddedParticipantPHIDs()) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransaction::TYPE_PARTICIPANTS)
|
||||
->setTransactionType(
|
||||
ConpherenceThreadParticipantsTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(array('+' => $this->getMailAddedParticipantPHIDs()));
|
||||
}
|
||||
|
||||
|
|
|
@ -1,73 +1,69 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Query class that answers the question:
|
||||
*
|
||||
* - Q: How many unread conpherences am I participating in?
|
||||
* - A:
|
||||
* id(new ConpherenceParticipantCountQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->withParticipationStatus(ConpherenceParticipationStatus::BEHIND)
|
||||
* ->execute();
|
||||
*/
|
||||
final class ConpherenceParticipantCountQuery
|
||||
extends PhabricatorOffsetPagedQuery {
|
||||
|
||||
private $participantPHIDs;
|
||||
private $participationStatus;
|
||||
private $unread;
|
||||
|
||||
public function withParticipantPHIDs(array $phids) {
|
||||
$this->participantPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withParticipationStatus($participation_status) {
|
||||
$this->participationStatus = $participation_status;
|
||||
public function withUnread($unread) {
|
||||
$this->unread = $unread;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$thread = new ConpherenceThread();
|
||||
$table = new ConpherenceParticipant();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$rows = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT COUNT(*) as count, participantPHID '.
|
||||
'FROM %T participant %Q %Q %Q',
|
||||
$conn,
|
||||
'SELECT COUNT(*) as count, participantPHID
|
||||
FROM %T participant JOIN %T thread
|
||||
ON participant.conpherencePHID = thread.phid %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildGroupByClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
$thread->getTableName(),
|
||||
$this->buildWhereClause($conn),
|
||||
$this->buildGroupByClause($conn),
|
||||
$this->buildLimitClause($conn));
|
||||
|
||||
return ipull($rows, 'count', 'participantPHID');
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
|
||||
if ($this->participantPHIDs) {
|
||||
if ($this->participantPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'participantPHID IN (%Ls)',
|
||||
$conn,
|
||||
'participant.participantPHID IN (%Ls)',
|
||||
$this->participantPHIDs);
|
||||
}
|
||||
|
||||
if ($this->participationStatus !== null) {
|
||||
if ($this->unread !== null) {
|
||||
if ($this->unread) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'participationStatus = %d',
|
||||
$this->participationStatus);
|
||||
$conn,
|
||||
'participant.seenMessageCount < thread.messageCount');
|
||||
} else {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'participant.seenMessageCount >= thread.messageCount');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildGroupByClause(AphrontDatabaseConnection $conn_r) {
|
||||
$group_by = qsprintf(
|
||||
$conn_r,
|
||||
private function buildGroupByClause(AphrontDatabaseConnection $conn) {
|
||||
return qsprintf(
|
||||
$conn,
|
||||
'GROUP BY participantPHID');
|
||||
|
||||
return $group_by;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,128 +1,50 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Query class that answers these questions:
|
||||
*
|
||||
* - Q: What are the conpherences to show when I land on /conpherence/ ?
|
||||
* - A:
|
||||
*
|
||||
* id(new ConpherenceParticipantQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->execute();
|
||||
*
|
||||
* - Q: What are the next set of conpherences as I scroll up (more recent) or
|
||||
* down (less recent) this list of conpherences?
|
||||
* - A:
|
||||
*
|
||||
* id(new ConpherenceParticipantQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->withParticipantCursor($top_participant)
|
||||
* ->setOrder(ConpherenceParticipantQuery::ORDER_NEWER)
|
||||
* ->execute();
|
||||
*
|
||||
* -or-
|
||||
*
|
||||
* id(new ConpherenceParticipantQuery())
|
||||
* ->withParticipantPHIDs(array($my_phid))
|
||||
* ->withParticipantCursor($bottom_participant)
|
||||
* ->setOrder(ConpherenceParticipantQuery::ORDER_OLDER)
|
||||
* ->execute();
|
||||
*
|
||||
* For counts of read, un-read, or all conpherences by participant, see
|
||||
* @{class:ConpherenceParticipantCountQuery}.
|
||||
*/
|
||||
final class ConpherenceParticipantQuery extends PhabricatorOffsetPagedQuery {
|
||||
|
||||
const LIMIT = 100;
|
||||
const ORDER_NEWER = 'newer';
|
||||
const ORDER_OLDER = 'older';
|
||||
|
||||
private $participantPHIDs;
|
||||
private $participantCursor;
|
||||
private $order = self::ORDER_OLDER;
|
||||
|
||||
public function withParticipantPHIDs(array $phids) {
|
||||
$this->participantPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withParticipantCursor(ConpherenceParticipant $participant) {
|
||||
$this->participantCursor = $participant;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setOrder($order) {
|
||||
$this->order = $order;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$table = new ConpherenceParticipant();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
$thread = new ConpherenceThread();
|
||||
|
||||
$conn = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T participant %Q %Q %Q',
|
||||
$conn,
|
||||
'SELECT * FROM %T participant JOIN %T thread
|
||||
ON participant.conpherencePHID = thread.phid %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
$thread->getTableName(),
|
||||
$this->buildWhereClause($conn),
|
||||
$this->buildOrderClause($conn),
|
||||
$this->buildLimitClause($conn));
|
||||
|
||||
$participants = $table->loadAllFromArray($data);
|
||||
|
||||
$participants = mpull($participants, null, 'getConpherencePHID');
|
||||
|
||||
if ($this->order == self::ORDER_NEWER) {
|
||||
$participants = array_reverse($participants);
|
||||
return $table->loadAllFromArray($data);
|
||||
}
|
||||
|
||||
return $participants;
|
||||
}
|
||||
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn_r) {
|
||||
protected function buildWhereClause(AphrontDatabaseConnection $conn) {
|
||||
$where = array();
|
||||
|
||||
if ($this->participantPHIDs) {
|
||||
if ($this->participantPHIDs !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
$conn,
|
||||
'participantPHID IN (%Ls)',
|
||||
$this->participantPHIDs);
|
||||
}
|
||||
|
||||
if ($this->participantCursor) {
|
||||
$date_touched = $this->participantCursor->getDateTouched();
|
||||
$id = $this->participantCursor->getID();
|
||||
if ($this->order == self::ORDER_OLDER) {
|
||||
$compare_date = '<';
|
||||
$compare_id = '<=';
|
||||
} else {
|
||||
$compare_date = '>';
|
||||
$compare_id = '>=';
|
||||
}
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'(dateTouched %Q %d OR (dateTouched = %d AND id %Q %d))',
|
||||
$compare_date,
|
||||
$date_touched,
|
||||
$date_touched,
|
||||
$compare_id,
|
||||
$id);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
|
||||
$order_word = ($this->order == self::ORDER_OLDER) ? 'DESC' : 'ASC';
|
||||
// if these are different direction we won't get as efficient a query
|
||||
// see http://dev.mysql.com/doc/refman/5.5/en/order-by-optimization.html
|
||||
$order = qsprintf(
|
||||
$conn_r,
|
||||
'ORDER BY dateTouched %Q, id %Q',
|
||||
$order_word,
|
||||
$order_word);
|
||||
|
||||
return $order;
|
||||
private function buildOrderClause(AphrontDatabaseConnection $conn) {
|
||||
return qsprintf(
|
||||
$conn,
|
||||
'ORDER BY thread.dateModified DESC, thread.id DESC, participant.id DESC');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -4,10 +4,7 @@ final class ConpherenceParticipant extends ConpherenceDAO {
|
|||
|
||||
protected $participantPHID;
|
||||
protected $conpherencePHID;
|
||||
protected $participationStatus;
|
||||
protected $behindTransactionPHID;
|
||||
protected $seenMessageCount;
|
||||
protected $dateTouched;
|
||||
protected $settings = array();
|
||||
|
||||
protected function getConfiguration() {
|
||||
|
@ -16,8 +13,6 @@ final class ConpherenceParticipant extends ConpherenceDAO {
|
|||
'settings' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'participationStatus' => 'uint32',
|
||||
'dateTouched' => 'epoch',
|
||||
'seenMessageCount' => 'uint64',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
|
@ -25,11 +20,8 @@ final class ConpherenceParticipant extends ConpherenceDAO {
|
|||
'columns' => array('conpherencePHID', 'participantPHID'),
|
||||
'unique' => true,
|
||||
),
|
||||
'unreadCount' => array(
|
||||
'columns' => array('participantPHID', 'participationStatus'),
|
||||
),
|
||||
'participationIndex' => array(
|
||||
'columns' => array('participantPHID', 'dateTouched', 'id'),
|
||||
'key_thread' => array(
|
||||
'columns' => array('participantPHID', 'conpherencePHID'),
|
||||
),
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
|
@ -39,12 +31,9 @@ final class ConpherenceParticipant extends ConpherenceDAO {
|
|||
return nonempty($this->settings, array());
|
||||
}
|
||||
|
||||
public function markUpToDate(
|
||||
ConpherenceThread $conpherence,
|
||||
ConpherenceTransaction $xaction) {
|
||||
public function markUpToDate(ConpherenceThread $conpherence) {
|
||||
|
||||
if (!$this->isUpToDate($conpherence)) {
|
||||
$this->setParticipationStatus(ConpherenceParticipationStatus::UP_TO_DATE);
|
||||
$this->setBehindTransactionPHID($xaction->getPHID());
|
||||
$this->setSeenMessageCount($conpherence->getMessageCount());
|
||||
$this->save();
|
||||
|
||||
|
@ -57,11 +46,7 @@ final class ConpherenceParticipant extends ConpherenceDAO {
|
|||
}
|
||||
|
||||
public function isUpToDate(ConpherenceThread $conpherence) {
|
||||
return
|
||||
($this->getSeenMessageCount() == $conpherence->getMessageCount())
|
||||
&&
|
||||
($this->getParticipationStatus() ==
|
||||
ConpherenceParticipationStatus::UP_TO_DATE);
|
||||
return ($this->getSeenMessageCount() == $conpherence->getMessageCount());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -72,6 +72,10 @@ final class ConpherenceThread extends ConpherenceDAO
|
|||
return 'Z'.$this->getID();
|
||||
}
|
||||
|
||||
public function getURI() {
|
||||
return '/'.$this->getMonogram();
|
||||
}
|
||||
|
||||
public function attachParticipants(array $participants) {
|
||||
assert_instances_of($participants, 'ConpherenceParticipant');
|
||||
$this->participants = $participants;
|
||||
|
@ -200,8 +204,13 @@ final class ConpherenceThread extends ConpherenceDAO
|
|||
}
|
||||
|
||||
$user_participation = $this->getParticipantIfExists($viewer->getPHID());
|
||||
$theme = ConpherenceRoomSettings::COLOR_LIGHT;
|
||||
if ($user_participation) {
|
||||
$user_seen_count = $user_participation->getSeenMessageCount();
|
||||
$participant = $this->getParticipant($viewer->getPHID());
|
||||
$settings = $participant->getSettings();
|
||||
$theme = idx($settings, 'theme', $theme);
|
||||
$theme_class = ConpherenceRoomSettings::getThemeClass($theme);
|
||||
} else {
|
||||
$user_seen_count = 0;
|
||||
}
|
||||
|
@ -217,6 +226,7 @@ final class ConpherenceThread extends ConpherenceDAO
|
|||
'unread_count' => $unread_count,
|
||||
'epoch' => $this->getDateModified(),
|
||||
'image' => $img_src,
|
||||
'theme' => $theme_class,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,8 +3,6 @@
|
|||
final class ConpherenceTransaction
|
||||
extends PhabricatorModularTransaction {
|
||||
|
||||
const TYPE_PARTICIPANTS = 'participants';
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'conpherence';
|
||||
}
|
||||
|
@ -21,81 +19,4 @@ final class ConpherenceTransaction
|
|||
return 'ConpherenceThreadTransactionType';
|
||||
}
|
||||
|
||||
public function getNoEffectDescription() {
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_PARTICIPANTS:
|
||||
return pht(
|
||||
'You can not add a participant who has already been added.');
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getNoEffectDescription();
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
$old = $this->getOldValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_PARTICIPANTS:
|
||||
return ($old === null);
|
||||
}
|
||||
|
||||
return parent::shouldHide();
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_PARTICIPANTS:
|
||||
$add = array_diff($new, $old);
|
||||
$rem = array_diff($old, $new);
|
||||
|
||||
if ($add && $rem) {
|
||||
$title = pht(
|
||||
'%s edited participant(s), added %d: %s; removed %d: %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
count($add),
|
||||
$this->renderHandleList($add),
|
||||
count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
} else if ($add) {
|
||||
$title = pht(
|
||||
'%s added %d participant(s): %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
count($add),
|
||||
$this->renderHandleList($add));
|
||||
} else {
|
||||
$title = pht(
|
||||
'%s removed %d participant(s): %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
}
|
||||
return $title;
|
||||
break;
|
||||
}
|
||||
|
||||
return parent::getTitle();
|
||||
}
|
||||
|
||||
public function getRequiredHandlePHIDs() {
|
||||
$phids = parent::getRequiredHandlePHIDs();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$phids[] = $this->getAuthorPHID();
|
||||
switch ($this->getTransactionType()) {
|
||||
case self::TYPE_PARTICIPANTS:
|
||||
$phids = array_merge($phids, $this->getOldValue());
|
||||
$phids = array_merge($phids, $this->getNewValue());
|
||||
break;
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,6 +10,7 @@ final class ConpherenceLayoutView extends AphrontTagView {
|
|||
private $search;
|
||||
private $messages;
|
||||
private $replyForm;
|
||||
private $theme = ConpherenceRoomSettings::COLOR_LIGHT;
|
||||
private $latestTransactionID;
|
||||
|
||||
public function setMessages($messages) {
|
||||
|
@ -56,6 +57,11 @@ final class ConpherenceLayoutView extends AphrontTagView {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function setTheme($theme) {
|
||||
$this->theme = $theme;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function setLatestTransactionID($id) {
|
||||
$this->latestTransactionID = $id;
|
||||
return $this;
|
||||
|
@ -66,6 +72,7 @@ final class ConpherenceLayoutView extends AphrontTagView {
|
|||
$classes[] = 'conpherence-layout';
|
||||
$classes[] = 'hide-widgets';
|
||||
$classes[] = 'conpherence-role-'.$this->role;
|
||||
$classes[] = ConpherenceRoomSettings::getThemeClass($this->theme);
|
||||
|
||||
return array(
|
||||
'id' => 'conpherence-main-layout',
|
||||
|
@ -105,6 +112,7 @@ final class ConpherenceLayoutView extends AphrontTagView {
|
|||
'canEditSelectedThread' => $can_edit_selected,
|
||||
'latestTransactionID' => $this->latestTransactionID,
|
||||
'role' => $this->role,
|
||||
'theme' => ConpherenceRoomSettings::getThemeClass($this->theme),
|
||||
'hasThreadList' => (bool)$this->threadView,
|
||||
'hasThread' => (bool)$this->messages,
|
||||
'hasWidgets' => false,
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
final class ConpherenceThreadListView extends AphrontView {
|
||||
|
||||
const SEE_MORE_LIMIT = 15;
|
||||
const SEE_ALL_LIMIT = 16;
|
||||
|
||||
private $baseURI;
|
||||
private $threads;
|
||||
|
@ -25,84 +25,11 @@ final class ConpherenceThreadListView extends AphrontView {
|
|||
->addClass('conpherence-menu')
|
||||
->setID('conpherence-menu');
|
||||
|
||||
$policy_objects = ConpherenceThread::loadViewPolicyObjects(
|
||||
$this->getUser(),
|
||||
$this->threads);
|
||||
|
||||
$this->addRoomsToMenu($menu, $this->threads, $policy_objects);
|
||||
|
||||
$menu = phutil_tag_div('phabricator-side-menu', $menu);
|
||||
$menu = phutil_tag_div('phui-basic-nav', $menu);
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
public function renderSingleThread(
|
||||
ConpherenceThread $thread,
|
||||
array $policy_objects) {
|
||||
assert_instances_of($policy_objects, 'PhabricatorPolicy');
|
||||
return $this->renderThread($thread, $policy_objects);
|
||||
}
|
||||
|
||||
private function renderThreadItem(
|
||||
ConpherenceThread $thread,
|
||||
array $policy_objects) {
|
||||
return id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_CUSTOM)
|
||||
->setName($this->renderThread($thread, $policy_objects));
|
||||
}
|
||||
|
||||
private function renderThread(
|
||||
ConpherenceThread $thread,
|
||||
array $policy_objects) {
|
||||
|
||||
$user = $this->getUser();
|
||||
|
||||
$uri = '/'.$thread->getMonogram();
|
||||
$data = $thread->getDisplayData($user);
|
||||
$icon = id(new PHUIIconView())
|
||||
->addClass('msr')
|
||||
->setIcon($thread->getPolicyIconName($policy_objects));
|
||||
$title = phutil_tag(
|
||||
'span',
|
||||
array(),
|
||||
array(
|
||||
$icon,
|
||||
$data['title'],
|
||||
));
|
||||
$subtitle = $data['subtitle'];
|
||||
$unread_count = $data['unread_count'];
|
||||
$epoch = $data['epoch'];
|
||||
$image = $data['image'];
|
||||
$dom_id = $thread->getPHID().'-nav-item';
|
||||
|
||||
return id(new ConpherenceMenuItemView())
|
||||
->setUser($user)
|
||||
->setTitle($title)
|
||||
->setSubtitle($subtitle)
|
||||
->setHref($uri)
|
||||
->setEpoch($epoch)
|
||||
->setImageURI($image)
|
||||
->setUnreadCount($unread_count)
|
||||
->setID($thread->getPHID().'-nav-item')
|
||||
->addSigil('conpherence-menu-click')
|
||||
->setMetadata(
|
||||
array(
|
||||
'title' => $data['title'],
|
||||
'id' => $dom_id,
|
||||
'threadID' => $thread->getID(),
|
||||
));
|
||||
}
|
||||
|
||||
private function addRoomsToMenu(
|
||||
PHUIListView $menu,
|
||||
array $rooms,
|
||||
array $policy_objects) {
|
||||
|
||||
$header = $this->renderMenuItemHeader();
|
||||
$header = $this->buildHeaderItemView();
|
||||
$menu->addMenuItem($header);
|
||||
|
||||
if (empty($rooms)) {
|
||||
// Blank State NUX
|
||||
if (empty($this->threads)) {
|
||||
$join_item = id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref('/conpherence/search/')
|
||||
|
@ -115,79 +42,70 @@ final class ConpherenceThreadListView extends AphrontView {
|
|||
->setWorkflow(true)
|
||||
->setName(pht('Create a Room'));
|
||||
$menu->addMenuItem($create_item);
|
||||
}
|
||||
|
||||
$rooms = $this->buildRoomItems($this->threads);
|
||||
foreach ($rooms as $room) {
|
||||
$menu->addMenuItem($room);
|
||||
}
|
||||
|
||||
$menu = phutil_tag_div('phabricator-side-menu', $menu);
|
||||
$menu = phutil_tag_div('phui-basic-nav', $menu);
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
$this->addThreadsToMenu($menu, $rooms, $policy_objects);
|
||||
return $menu;
|
||||
private function renderThreadItem(
|
||||
ConpherenceThread $thread) {
|
||||
|
||||
$user = $this->getUser();
|
||||
$data = $thread->getDisplayData($user);
|
||||
$dom_id = $thread->getPHID().'-nav-item';
|
||||
|
||||
return id(new PHUIListItemView())
|
||||
->setName($data['title'])
|
||||
->setHref('/'.$thread->getMonogram())
|
||||
->setProfileImage($data['image'])
|
||||
->setCount($data['unread_count'])
|
||||
->setType(PHUIListItemView::TYPE_CUSTOM)
|
||||
->setID($thread->getPHID().'-nav-item')
|
||||
->addSigil('conpherence-menu-click')
|
||||
->setMetadata(
|
||||
array(
|
||||
'title' => $data['title'],
|
||||
'id' => $dom_id,
|
||||
'threadID' => $thread->getID(),
|
||||
'theme' => $data['theme'],
|
||||
));
|
||||
}
|
||||
|
||||
private function addThreadsToMenu(
|
||||
PHUIListView $menu,
|
||||
array $threads,
|
||||
array $policy_objects) {
|
||||
private function buildRoomItems(array $threads) {
|
||||
|
||||
// If we have self::SEE_MORE_LIMIT or less, we can just render
|
||||
// all the threads at once. Otherwise, we render a "See more"
|
||||
// UI element, which toggles a show / hide on the remaining rooms
|
||||
$items = array();
|
||||
$show_threads = $threads;
|
||||
$more_threads = array();
|
||||
if (count($threads) > self::SEE_MORE_LIMIT) {
|
||||
$show_threads = array_slice($threads, 0, self::SEE_MORE_LIMIT);
|
||||
$more_threads = array_slice($threads, self::SEE_MORE_LIMIT);
|
||||
$all_threads = false;
|
||||
if (count($threads) > self::SEE_ALL_LIMIT) {
|
||||
$show_threads = array_slice($threads, 0, self::SEE_ALL_LIMIT);
|
||||
$all_threads = true;
|
||||
}
|
||||
|
||||
foreach ($show_threads as $thread) {
|
||||
$item = $this->renderThreadItem($thread, $policy_objects);
|
||||
$menu->addMenuItem($item);
|
||||
$items[] = $this->renderThreadItem($thread);
|
||||
}
|
||||
|
||||
if ($more_threads) {
|
||||
$search_uri = '/conpherence/search/query/participant/';
|
||||
$sigil = 'more-room';
|
||||
|
||||
$more_item = id(new PHUIListItemView())
|
||||
// Send them to application search here
|
||||
if ($all_threads) {
|
||||
$items[] = id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref($search_uri)
|
||||
->addSigil('conpherence-menu-see-more')
|
||||
->setMetadata(array('moreSigil' => $sigil))
|
||||
->setName(pht('See More'));
|
||||
$menu->addMenuItem($more_item);
|
||||
$show_more_threads = $more_threads;
|
||||
$even_more_threads = array();
|
||||
if (count($more_threads) > self::SEE_MORE_LIMIT) {
|
||||
$show_more_threads = array_slice(
|
||||
$more_threads,
|
||||
0,
|
||||
self::SEE_MORE_LIMIT);
|
||||
$even_more_threads = array_slice(
|
||||
$more_threads,
|
||||
self::SEE_MORE_LIMIT);
|
||||
}
|
||||
foreach ($show_more_threads as $thread) {
|
||||
$item = $this->renderThreadItem($thread, $policy_objects)
|
||||
->addSigil($sigil)
|
||||
->addClass('hidden');
|
||||
$menu->addMenuItem($item);
|
||||
->setHref('/conpherence/search/query/participant/')
|
||||
->setIcon('fa-external-link')
|
||||
->setName(pht('See All Joined'));
|
||||
}
|
||||
|
||||
if ($even_more_threads) {
|
||||
// kick them to application search here
|
||||
$even_more_item = id(new PHUIListItemView())
|
||||
->setType(PHUIListItemView::TYPE_LINK)
|
||||
->setHref($search_uri)
|
||||
->addSigil($sigil)
|
||||
->addClass('hidden')
|
||||
->setName(pht('See More'));
|
||||
$menu->addMenuItem($even_more_item);
|
||||
}
|
||||
return $items;
|
||||
}
|
||||
|
||||
return $menu;
|
||||
}
|
||||
|
||||
private function renderMenuItemHeader() {
|
||||
private function buildHeaderItemView() {
|
||||
$rooms = phutil_tag(
|
||||
'a',
|
||||
array(
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceThreadParticipantsTransaction
|
||||
extends ConpherenceThreadTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'participants';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getParticipantPHIDs();
|
||||
}
|
||||
|
||||
public function generateNewValue($object, $value) {
|
||||
$old = $this->generateOldValue($object);
|
||||
return $this->getPHIDList($old, $value);
|
||||
}
|
||||
|
||||
public function applyExternalEffects($object, $value) {
|
||||
$participants = $object->getParticipants();
|
||||
|
||||
$old = array_keys($participants);
|
||||
$new = $value;
|
||||
|
||||
$add_map = array_fuse(array_diff($new, $old));
|
||||
$rem_map = array_fuse(array_diff($old, $new));
|
||||
|
||||
foreach ($rem_map as $phid) {
|
||||
$remove_participant = $participants[$phid];
|
||||
$remove_participant->delete();
|
||||
unset($participants[$phid]);
|
||||
}
|
||||
|
||||
foreach ($add_map as $phid) {
|
||||
if (isset($participants[$phid])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$participants[$phid] = id(new ConpherenceParticipant())
|
||||
->setConpherencePHID($object->getPHID())
|
||||
->setParticipantPHID($phid)
|
||||
->setSeenMessageCount(0)
|
||||
->save();
|
||||
}
|
||||
|
||||
$object->attachParticipants($participants);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
$add = array_diff($new, $old);
|
||||
$rem = array_diff($old, $new);
|
||||
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
|
||||
if ($add && $rem) {
|
||||
return pht(
|
||||
'%s edited participant(s), added %d: %s; removed %d: %s.',
|
||||
$this->renderAuthor(),
|
||||
count($add),
|
||||
$this->renderHandleList($add),
|
||||
count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
} else if ((in_array($author_phid, $add)) && (count($add) == 1)) {
|
||||
return pht(
|
||||
'%s joined the room.',
|
||||
$this->renderAuthor());
|
||||
} else if ((in_array($author_phid, $rem)) && (count($rem) == 1)) {
|
||||
return pht(
|
||||
'%s left the room.',
|
||||
$this->renderAuthor());
|
||||
} else if ($add) {
|
||||
return pht(
|
||||
'%s added %d participant(s): %s.',
|
||||
$this->renderAuthor(),
|
||||
count($add),
|
||||
$this->renderHandleList($add));
|
||||
} else {
|
||||
return pht(
|
||||
'%s removed %d participant(s): %s.',
|
||||
$this->renderAuthor(),
|
||||
count($rem),
|
||||
$this->renderHandleList($rem));
|
||||
}
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
foreach ($xactions as $xaction) {
|
||||
$old = $object->getParticipantPHIDs();
|
||||
|
||||
$new = $xaction->getNewValue();
|
||||
$new = $this->getPHIDList($old, $new);
|
||||
|
||||
$add_map = array_fuse(array_diff($new, $old));
|
||||
$rem_map = array_fuse(array_diff($old, $new));
|
||||
|
||||
foreach ($add_map as $user_phid) {
|
||||
$user = id(new PhabricatorPeopleQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withPHIDs(array($user_phid))
|
||||
->executeOne();
|
||||
if (!$user) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Participant PHID "%s" is not a valid user PHID.',
|
||||
$user_phid));
|
||||
continue;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,78 @@
|
|||
<?php
|
||||
|
||||
final class DarkConsoleRealtimePlugin extends DarkConsolePlugin {
|
||||
|
||||
public function getName() {
|
||||
return pht('Realtime');
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getDescription() {
|
||||
return pht('Debugging console for real-time notifications.');
|
||||
}
|
||||
|
||||
public function renderPanel() {
|
||||
$frame = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => 'dark-console-realtime-log',
|
||||
'class' => 'dark-console-log-frame',
|
||||
));
|
||||
|
||||
$reconnect_label = pht('Reconnect');
|
||||
$replay_label = pht('Replay');
|
||||
$repaint_label = pht('Repaint');
|
||||
|
||||
$buttons = phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'dark-console-realtime-actions',
|
||||
),
|
||||
array(
|
||||
id(new PHUIButtonView())
|
||||
->setIcon('fa-refresh')
|
||||
->setColor(PHUIButtonView::GREY)
|
||||
->setText($reconnect_label)
|
||||
->addSigil('dark-console-realtime-action')
|
||||
->setMetadata(
|
||||
array(
|
||||
'action' => 'reconnect',
|
||||
'label' => $reconnect_label,
|
||||
)),
|
||||
id(new PHUIButtonView())
|
||||
->setIcon('fa-backward')
|
||||
->setColor(PHUIButtonView::GREY)
|
||||
->setText($replay_label)
|
||||
->addSigil('dark-console-realtime-action')
|
||||
->setMetadata(
|
||||
array(
|
||||
'action' => 'replay',
|
||||
'label' => $replay_label,
|
||||
)),
|
||||
id(new PHUIButtonView())
|
||||
->setIcon('fa-paint-brush')
|
||||
->setColor(PHUIButtonView::GREY)
|
||||
->setText($repaint_label)
|
||||
->addSigil('dark-console-realtime-action')
|
||||
->setMetadata(
|
||||
array(
|
||||
'action' => 'repaint',
|
||||
'label' => $repaint_label,
|
||||
)),
|
||||
));
|
||||
|
||||
return phutil_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'dark-console-realtime',
|
||||
),
|
||||
array(
|
||||
$buttons,
|
||||
$frame,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
|
@ -55,80 +55,101 @@ final class DifferentialReviewersView extends AphrontView {
|
|||
|
||||
$item->setHighlighted($reviewer->hasAuthority($viewer));
|
||||
|
||||
// If someone other than the reviewer acted on the reviewer's behalf,
|
||||
// show who is responsible for the current state. This is usually a
|
||||
// user accepting for a package or project.
|
||||
$authority_phid = $reviewer->getLastActorPHID();
|
||||
if ($authority_phid && ($authority_phid !== $phid)) {
|
||||
$authority_name = $viewer->renderHandle($authority_phid)
|
||||
->setAsText(true);
|
||||
} else {
|
||||
$authority_name = null;
|
||||
}
|
||||
|
||||
switch ($reviewer->getReviewerStatus()) {
|
||||
case DifferentialReviewerStatus::STATUS_ADDED:
|
||||
if ($comment_phid) {
|
||||
if ($is_current_comment) {
|
||||
$item->setIcon(
|
||||
'fa-comment',
|
||||
'blue',
|
||||
pht('Commented'));
|
||||
$icon = 'fa-comment';
|
||||
$color = 'blue';
|
||||
$label = pht('Commented');
|
||||
} else {
|
||||
$item->setIcon(
|
||||
'fa-comment-o',
|
||||
'bluegrey',
|
||||
pht('Commented Previously'));
|
||||
$icon = 'fa-comment-o';
|
||||
$color = 'bluegrey';
|
||||
$label = pht('Commented Previously');
|
||||
}
|
||||
} else {
|
||||
$item->setIcon(
|
||||
PHUIStatusItemView::ICON_OPEN,
|
||||
'bluegrey',
|
||||
pht('Review Requested'));
|
||||
$icon = PHUIStatusItemView::ICON_OPEN;
|
||||
$color = 'bluegrey';
|
||||
$label = pht('Review Requested');
|
||||
}
|
||||
break;
|
||||
|
||||
case DifferentialReviewerStatus::STATUS_ACCEPTED:
|
||||
if ($is_current_action) {
|
||||
$item->setIcon(
|
||||
PHUIStatusItemView::ICON_ACCEPT,
|
||||
'green',
|
||||
pht('Accepted'));
|
||||
$icon = PHUIStatusItemView::ICON_ACCEPT;
|
||||
$color = 'green';
|
||||
if ($authority_name !== null) {
|
||||
$label = pht('Accepted (by %s)', $authority_name);
|
||||
} else {
|
||||
$item->setIcon(
|
||||
'fa-check-circle-o',
|
||||
'bluegrey',
|
||||
pht('Accepted Prior Diff'));
|
||||
$label = pht('Accepted');
|
||||
}
|
||||
} else {
|
||||
$icon = 'fa-check-circle-o';
|
||||
$color = 'bluegrey';
|
||||
if ($authority_name !== null) {
|
||||
$label = pht('Accepted Prior Diff (by %s)', $authority_name);
|
||||
} else {
|
||||
$label = pht('Accepted Prior Diff');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DifferentialReviewerStatus::STATUS_REJECTED:
|
||||
if ($is_current_action) {
|
||||
$item->setIcon(
|
||||
PHUIStatusItemView::ICON_REJECT,
|
||||
'red',
|
||||
pht('Requested Changes'));
|
||||
$icon = PHUIStatusItemView::ICON_REJECT;
|
||||
$color = 'red';
|
||||
if ($authority_name !== null) {
|
||||
$label = pht('Requested Changes (by %s)', $authority_name);
|
||||
} else {
|
||||
$item->setIcon(
|
||||
'fa-times-circle-o',
|
||||
'bluegrey',
|
||||
pht('Requested Changes to Prior Diff'));
|
||||
$label = pht('Requested Changes');
|
||||
}
|
||||
} else {
|
||||
$icon = 'fa-times-circle-o';
|
||||
$color = 'bluegrey';
|
||||
if ($authority_name !== null) {
|
||||
$label = pht(
|
||||
'Requested Changes to Prior Diff (by %s)',
|
||||
$authority_name);
|
||||
} else {
|
||||
$label = pht('Requested Changes to Prior Diff');
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DifferentialReviewerStatus::STATUS_BLOCKING:
|
||||
$item->setIcon(
|
||||
PHUIStatusItemView::ICON_MINUS,
|
||||
'red',
|
||||
pht('Blocking Review'));
|
||||
$icon = PHUIStatusItemView::ICON_MINUS;
|
||||
$color = 'red';
|
||||
$label = pht('Blocking Review');
|
||||
break;
|
||||
|
||||
case DifferentialReviewerStatus::STATUS_RESIGNED:
|
||||
$item->setIcon(
|
||||
'fa-times',
|
||||
'grey',
|
||||
pht('Resigned'));
|
||||
$icon = 'fa-times';
|
||||
$color = 'grey';
|
||||
$label = pht('Resigned');
|
||||
break;
|
||||
|
||||
default:
|
||||
$item->setIcon(
|
||||
PHUIStatusItemView::ICON_QUESTION,
|
||||
'bluegrey',
|
||||
pht('%s?', $reviewer->getReviewerStatus()));
|
||||
$icon = PHUIStatusItemView::ICON_QUESTION;
|
||||
$color = 'bluegrey';
|
||||
$label = pht('Unknown ("%s")', $reviewer->getReviewerStatus());
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
$item->setIcon($icon, $color, $label);
|
||||
$item->setTarget($handle->renderHovercardLink());
|
||||
|
||||
$view->addItem($item);
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,8 @@ final class DiffusionBranchTableController extends DiffusionController {
|
|||
$content = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($repository->getName())
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
->setTable($table)
|
||||
->setPager($pager);
|
||||
}
|
||||
|
||||
$crumbs = $this->buildCrumbs(
|
||||
|
@ -66,8 +67,6 @@ final class DiffusionBranchTableController extends DiffusionController {
|
|||
));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Branches'))
|
||||
->setHeaderIcon('fa-code-fork');
|
||||
|
@ -76,7 +75,6 @@ final class DiffusionBranchTableController extends DiffusionController {
|
|||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$content,
|
||||
$pager_box,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
|
|
|
@ -370,7 +370,8 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
$browse_panel = id(new PHUIObjectBoxView())
|
||||
->setHeader($browse_header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($browse_table);
|
||||
->setTable($browse_table)
|
||||
->setPager($pager);
|
||||
|
||||
$browse_panel->setShowHide(
|
||||
array(pht('Show Search')),
|
||||
|
@ -395,7 +396,6 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
'view' => 'browse',
|
||||
));
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
|
@ -411,7 +411,6 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
array(
|
||||
$open_revisions,
|
||||
$readme,
|
||||
$pager_box,
|
||||
));
|
||||
|
||||
if ($details) {
|
||||
|
@ -489,14 +488,12 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
nonempty($drequest->getPath(), '/'));
|
||||
}
|
||||
|
||||
$box = id(new PHUIObjectBoxView())
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeaderText($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
->setTable($table)
|
||||
->setPager($pager);
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
|
||||
return array($box, $pager_box);
|
||||
}
|
||||
|
||||
private function renderGrepResults(array $results, $pattern) {
|
||||
|
@ -1775,9 +1772,8 @@ final class DiffusionBrowseController extends DiffusionController {
|
|||
}
|
||||
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Open Revisions'))
|
||||
->setSubheader(
|
||||
pht('Recently updated open revisions affecting this file.'));
|
||||
->setHeader(pht('Recently Open Revisions'))
|
||||
->setHeaderIcon('fa-gear');
|
||||
|
||||
$view = id(new DifferentialRevisionListView())
|
||||
->setHeader($header)
|
||||
|
|
|
@ -92,6 +92,7 @@ final class DiffusionCompareController extends DiffusionController {
|
|||
array(
|
||||
'view' => 'compare',
|
||||
));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$pager = id(new PHUIPagerView())
|
||||
->readFromRequest($request);
|
||||
|
@ -310,16 +311,11 @@ final class DiffusionCompareController extends DiffusionController {
|
|||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Commits'));
|
||||
|
||||
$object_box = id(new PHUIObjectBoxView())
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($history_table);
|
||||
->setTable($history_table)
|
||||
->setPager($pager);
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
|
||||
return array(
|
||||
$object_box,
|
||||
$pager_box,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -317,12 +317,6 @@ abstract class DiffusionController extends PhabricatorController {
|
|||
->appendChild($body);
|
||||
}
|
||||
|
||||
protected function renderTablePagerBox(PHUIPagerView $pager) {
|
||||
return id(new PHUIBoxView())
|
||||
->addMargin(PHUI::MARGIN_LARGE)
|
||||
->appendChild($pager);
|
||||
}
|
||||
|
||||
protected function renderCommitHashTag(DiffusionRequest $drequest) {
|
||||
$stable_commit = $drequest->getStableCommit();
|
||||
$commit = phutil_tag(
|
||||
|
|
|
@ -57,7 +57,8 @@ final class DiffusionHistoryController extends DiffusionController {
|
|||
$history_panel = id(new PHUIObjectBoxView())
|
||||
->setHeader($history_header)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($history_table);
|
||||
->setTable($history_table)
|
||||
->setPager($pager);
|
||||
|
||||
$header = $this->buildHeader($drequest);
|
||||
|
||||
|
@ -69,13 +70,10 @@ final class DiffusionHistoryController extends DiffusionController {
|
|||
));
|
||||
$crumbs->setBorder(true);
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$history_panel,
|
||||
$pager_box,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
|
|
|
@ -456,7 +456,8 @@ final class DiffusionLintController extends DiffusionController {
|
|||
$content[] = id(new PHUIObjectBoxView())
|
||||
->setHeaderText(pht('Lint Details'))
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($table);
|
||||
->setTable($table)
|
||||
->setPager($pager);
|
||||
|
||||
$crumbs = $this->buildCrumbs(
|
||||
array(
|
||||
|
@ -465,7 +466,6 @@ final class DiffusionLintController extends DiffusionController {
|
|||
'view' => 'lint',
|
||||
));
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader(pht('Lint: %s', $drequest->getRepository()->getDisplayName()))
|
||||
->setHeaderIcon('fa-code');
|
||||
|
@ -474,7 +474,6 @@ final class DiffusionLintController extends DiffusionController {
|
|||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$content,
|
||||
$pager_box,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
|
|
|
@ -354,9 +354,9 @@ final class DiffusionRepositoryController extends DiffusionController {
|
|||
}
|
||||
|
||||
if ($repository->isSVN()) {
|
||||
$label = pht('Checkout');
|
||||
$label = phutil_tag_div('diffusion-clone-label', pht('Checkout'));
|
||||
} else {
|
||||
$label = pht('Clone');
|
||||
$label = phutil_tag_div('diffusion-clone-label', pht('Clone'));
|
||||
}
|
||||
|
||||
$view->addProperty(
|
||||
|
@ -686,15 +686,10 @@ final class DiffusionRepositoryController extends DiffusionController {
|
|||
$pager->setURI($browse_uri, 'offset');
|
||||
|
||||
if ($pager->willShowPagingControls()) {
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
} else {
|
||||
$pager_box = null;
|
||||
$browse_panel->setPager($pager);
|
||||
}
|
||||
|
||||
return array(
|
||||
$browse_panel,
|
||||
$pager_box,
|
||||
);
|
||||
return $browse_panel;
|
||||
}
|
||||
|
||||
private function renderCloneURI(
|
||||
|
|
|
@ -87,15 +87,13 @@ final class DiffusionTagListController extends DiffusionController {
|
|||
$box = id(new PHUIObjectBoxView())
|
||||
->setHeaderText($repository->getDisplayName())
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->setTable($view);
|
||||
|
||||
$pager_box = $this->renderTablePagerBox($pager);
|
||||
->setTable($view)
|
||||
->setPager($pager);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
->setFooter(array(
|
||||
$box,
|
||||
$pager_box,
|
||||
));
|
||||
|
||||
return $this->newPage()
|
||||
|
|
|
@ -73,18 +73,6 @@ final class DiffusionReadmeView extends DiffusionView {
|
|||
->getOutput($markup_object, $markup_field);
|
||||
|
||||
$engine = $markup_object->newMarkupEngine($markup_field);
|
||||
$toc = PhutilRemarkupHeaderBlockRule::renderTableOfContents($engine);
|
||||
if ($toc) {
|
||||
$toc = phutil_tag_div(
|
||||
'phabricator-remarkup-toc',
|
||||
array(
|
||||
phutil_tag_div(
|
||||
'phabricator-remarkup-toc-header',
|
||||
pht('Table of Contents')),
|
||||
$toc,
|
||||
));
|
||||
$content = array($toc, $content);
|
||||
}
|
||||
|
||||
$readme_content = $content;
|
||||
$class = null;
|
||||
|
@ -106,15 +94,13 @@ final class DiffusionReadmeView extends DiffusionView {
|
|||
}
|
||||
|
||||
$readme_content = phutil_tag_div($class, $readme_content);
|
||||
$header = id(new PHUIHeaderView())
|
||||
->setHeader($readme_name);
|
||||
|
||||
$document = id(new PHUIDocumentViewPro())
|
||||
->setFluid(true)
|
||||
->appendChild($readme_content);
|
||||
|
||||
return id(new PHUIObjectBoxView())
|
||||
->setHeader($header)
|
||||
->setHeaderText($readme_name)
|
||||
->setBackground(PHUIObjectBoxView::BLUE_PROPERTY)
|
||||
->appendChild($document)
|
||||
->addClass('diffusion-readme-view');
|
||||
}
|
||||
|
|
|
@ -62,24 +62,8 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
|
|||
// an initial request for bytes 0-1 of the audio file, and things go south
|
||||
// if we can't respond with a 206 Partial Content.
|
||||
$range = $request->getHTTPHeader('range');
|
||||
if ($range) {
|
||||
$matches = null;
|
||||
if (preg_match('/^bytes=(\d+)-(\d*)$/', $range, $matches)) {
|
||||
// Note that the "Range" header specifies bytes differently than
|
||||
// we do internally: the range 0-1 has 2 bytes (byte 0 and byte 1).
|
||||
$begin = (int)$matches[1];
|
||||
|
||||
// The "Range" may be "200-299" or "200-", meaning "until end of file".
|
||||
if (strlen($matches[2])) {
|
||||
$range_end = (int)$matches[2];
|
||||
$end = $range_end + 1;
|
||||
} else {
|
||||
$range_end = null;
|
||||
}
|
||||
|
||||
$response->setHTTPResponseCode(206);
|
||||
$response->setRange($begin, $range_end);
|
||||
}
|
||||
if (strlen($range)) {
|
||||
list($begin, $end) = $response->parseHTTPRange($range);
|
||||
}
|
||||
|
||||
$is_viewable = $file->isViewableInBrowser();
|
||||
|
@ -143,6 +127,7 @@ final class PhabricatorFileDataController extends PhabricatorFileController {
|
|||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($this->phid))
|
||||
->withIsDeleted(false)
|
||||
->executeOne();
|
||||
|
||||
if (!$file) {
|
||||
|
|
|
@ -9,6 +9,7 @@ final class PhabricatorFileDeleteController extends PhabricatorFileController {
|
|||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->withIsDeleted(false)
|
||||
->requireCapabilities(
|
||||
array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
|
@ -25,7 +26,19 @@ final class PhabricatorFileDeleteController extends PhabricatorFileController {
|
|||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$file->delete();
|
||||
$xactions = array();
|
||||
|
||||
$xactions[] = id(new PhabricatorFileTransaction())
|
||||
->setTransactionType(PhabricatorFileDeleteTransaction::TRANSACTIONTYPE)
|
||||
->setNewValue(true);
|
||||
|
||||
id(new PhabricatorFileEditor())
|
||||
->setActor($viewer)
|
||||
->setContentSourceFromRequest($request)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setContinueOnMissingFields(true)
|
||||
->applyTransactions($file, $xactions);
|
||||
|
||||
return id(new AphrontRedirectResponse())->setURI('/file/');
|
||||
}
|
||||
|
||||
|
|
|
@ -15,6 +15,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
|||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($phid))
|
||||
->withIsDeleted(false)
|
||||
->executeOne();
|
||||
|
||||
if (!$file) {
|
||||
|
@ -25,6 +26,7 @@ final class PhabricatorFileInfoController extends PhabricatorFileController {
|
|||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer($viewer)
|
||||
->withIDs(array($id))
|
||||
->withIsDeleted(false)
|
||||
->executeOne();
|
||||
if (!$file) {
|
||||
return new Aphront404Response();
|
||||
|
|
|
@ -36,7 +36,9 @@ final class PhabricatorFileEditEngine
|
|||
}
|
||||
|
||||
protected function newObjectQuery() {
|
||||
return new PhabricatorFileQuery();
|
||||
$query = new PhabricatorFileQuery();
|
||||
$query->withIsDeleted(false);
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function getObjectCreateTitleText($object) {
|
||||
|
|
|
@ -71,7 +71,7 @@ final class PhabricatorFileEditor
|
|||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -129,7 +129,7 @@ final class PhabricatorChunkedFileStorageEngine
|
|||
foreach ($chunks as $chunk) {
|
||||
$chunk->save();
|
||||
}
|
||||
$file->save();
|
||||
$file->saveAndIndex();
|
||||
$file->saveTransaction();
|
||||
|
||||
return $file;
|
||||
|
|
|
@ -18,8 +18,10 @@ final class PhabricatorFileTemporaryGarbageCollector
|
|||
'ttl < %d LIMIT 100',
|
||||
PhabricatorTime::getNow());
|
||||
|
||||
$engine = new PhabricatorDestructionEngine();
|
||||
|
||||
foreach ($files as $file) {
|
||||
$file->delete();
|
||||
$engine->destroyObject($file);
|
||||
}
|
||||
|
||||
return (count($files) == 100);
|
||||
|
|
|
@ -15,6 +15,7 @@ final class PhabricatorFileQuery
|
|||
private $maxLength;
|
||||
private $names;
|
||||
private $isPartial;
|
||||
private $isDeleted;
|
||||
private $needTransforms;
|
||||
private $builtinKeys;
|
||||
|
||||
|
@ -119,6 +120,17 @@ final class PhabricatorFileQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withIsDeleted($deleted) {
|
||||
$this->isDeleted = $deleted;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withNameNgrams($ngrams) {
|
||||
return $this->withNgramsConstraint(
|
||||
id(new PhabricatorFileNameNgrams()),
|
||||
$ngrams);
|
||||
}
|
||||
|
||||
public function showOnlyExplicitUploads($explicit_uploads) {
|
||||
$this->explicitUploads = $explicit_uploads;
|
||||
return $this;
|
||||
|
@ -390,6 +402,13 @@ final class PhabricatorFileQuery
|
|||
(int)$this->isPartial);
|
||||
}
|
||||
|
||||
if ($this->isDeleted !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'isDeleted = %d',
|
||||
(int)$this->isDeleted);
|
||||
}
|
||||
|
||||
if ($this->builtinKeys !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
|
|
|
@ -16,7 +16,9 @@ final class PhabricatorFileSearchEngine
|
|||
}
|
||||
|
||||
public function newQuery() {
|
||||
return new PhabricatorFileQuery();
|
||||
$query = new PhabricatorFileQuery();
|
||||
$query->withIsDeleted(false);
|
||||
return $query;
|
||||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
|
@ -38,6 +40,10 @@ final class PhabricatorFileSearchEngine
|
|||
id(new PhabricatorSearchDateField())
|
||||
->setKey('createdEnd')
|
||||
->setLabel(pht('Created Before')),
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Name Contains'))
|
||||
->setKey('name')
|
||||
->setDescription(pht('Search for files by name substring.')),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -68,6 +74,10 @@ final class PhabricatorFileSearchEngine
|
|||
$query->withDateCreatedBefore($map['createdEnd']);
|
||||
}
|
||||
|
||||
if ($map['name'] !== null) {
|
||||
$query->withNameNgrams($map['name']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
|
|
@ -28,7 +28,9 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
PhabricatorFlaggableInterface,
|
||||
PhabricatorPolicyInterface,
|
||||
PhabricatorDestructibleInterface,
|
||||
PhabricatorConduitResultInterface {
|
||||
PhabricatorConduitResultInterface,
|
||||
PhabricatorIndexableInterface,
|
||||
PhabricatorNgramsInterface {
|
||||
|
||||
const METADATA_IMAGE_WIDTH = 'width';
|
||||
const METADATA_IMAGE_HEIGHT = 'height';
|
||||
|
@ -39,6 +41,9 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
const METADATA_STORAGE = 'storage';
|
||||
const METADATA_INTEGRITY = 'integrity';
|
||||
|
||||
const STATUS_ACTIVE = 'active';
|
||||
const STATUS_DELETED = 'deleted';
|
||||
|
||||
protected $name;
|
||||
protected $mimeType;
|
||||
protected $byteSize;
|
||||
|
@ -57,6 +62,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
protected $isExplicitUpload = 1;
|
||||
protected $viewPolicy = PhabricatorPolicies::POLICY_USER;
|
||||
protected $isPartial = 0;
|
||||
protected $isDeleted = 0;
|
||||
|
||||
private $objects = self::ATTACHABLE;
|
||||
private $objectPHIDs = self::ATTACHABLE;
|
||||
|
@ -87,7 +93,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
'metadata' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'text255?',
|
||||
'name' => 'sort255?',
|
||||
'mimeType' => 'text255?',
|
||||
'byteSize' => 'uint64',
|
||||
'storageEngine' => 'text32',
|
||||
|
@ -101,6 +107,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
'mailKey' => 'bytes20',
|
||||
'isPartial' => 'bool',
|
||||
'builtinKey' => 'text64?',
|
||||
'isDeleted' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
'key_phid' => null,
|
||||
|
@ -146,6 +153,12 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return parent::save();
|
||||
}
|
||||
|
||||
public function saveAndIndex() {
|
||||
$this->save();
|
||||
PhabricatorSearchWorker::queueDocumentForIndexing($this->getPHID());
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getMonogram() {
|
||||
return 'F'.$this->getID();
|
||||
}
|
||||
|
@ -232,7 +245,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
|
||||
$new_file->readPropertiesFromParameters($params);
|
||||
|
||||
$new_file->save();
|
||||
$new_file->saveAndIndex();
|
||||
|
||||
return $new_file;
|
||||
}
|
||||
|
@ -388,7 +401,7 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
// Do nothing
|
||||
}
|
||||
|
||||
$file->save();
|
||||
$file->saveAndIndex();
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
@ -1585,4 +1598,14 @@ final class PhabricatorFile extends PhabricatorFileDAO
|
|||
return array();
|
||||
}
|
||||
|
||||
/* -( PhabricatorNgramInterface )------------------------------------------ */
|
||||
|
||||
|
||||
public function newNgrams() {
|
||||
return array(
|
||||
id(new PhabricatorFileNameNgrams())
|
||||
->setValue($this->getName()),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
18
src/applications/files/storage/PhabricatorFileNameNgrams.php
Normal file
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFileNameNgrams
|
||||
extends PhabricatorSearchNgrams {
|
||||
|
||||
public function getNgramKey() {
|
||||
return 'filename';
|
||||
}
|
||||
|
||||
public function getColumnName() {
|
||||
return 'name';
|
||||
}
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'file';
|
||||
}
|
||||
|
||||
}
|
|
@ -145,7 +145,7 @@ abstract class PhabricatorFileUploadSource
|
|||
}
|
||||
|
||||
$file = PhabricatorFile::newChunkedFile($engine, $length, $parameters);
|
||||
$file->save();
|
||||
$file->saveAndIndex();
|
||||
|
||||
$rope = $this->getRope();
|
||||
|
||||
|
|
31
src/applications/files/worker/FileDeletionWorker.php
Normal file
|
@ -0,0 +1,31 @@
|
|||
<?php
|
||||
|
||||
final class FileDeletionWorker extends PhabricatorWorker {
|
||||
|
||||
private function loadFile() {
|
||||
$phid = idx($this->getTaskData(), 'objectPHID');
|
||||
if (!$phid) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht('No "%s" in task data.', 'objectPHID'));
|
||||
}
|
||||
|
||||
$file = id(new PhabricatorFileQuery())
|
||||
->setViewer(PhabricatorUser::getOmnipotentUser())
|
||||
->withPHIDs(array($phid))
|
||||
->executeOne();
|
||||
|
||||
if (!$file) {
|
||||
throw new PhabricatorWorkerPermanentFailureException(
|
||||
pht('File "%s" does not exist.', $phid));
|
||||
}
|
||||
|
||||
return $file;
|
||||
}
|
||||
|
||||
protected function doWork() {
|
||||
$file = $this->loadFile();
|
||||
$engine = new PhabricatorDestructionEngine();
|
||||
$engine->destroyObject($file);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,45 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorFileDeleteTransaction
|
||||
extends PhabricatorFileTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'file:delete';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return PhabricatorFile::STATUS_ACTIVE;
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$file = $object;
|
||||
// Mark the file for deletion, save it, and schedule a worker to
|
||||
// sweep by later and pick it up.
|
||||
$file->setIsDeleted(true);
|
||||
|
||||
PhabricatorWorker::scheduleTask(
|
||||
'FileDeletionWorker',
|
||||
array('objectPHID' => $file->getPHID()),
|
||||
array('priority' => PhabricatorWorker::PRIORITY_BULK));
|
||||
}
|
||||
|
||||
public function getIcon() {
|
||||
return 'fa-ban';
|
||||
}
|
||||
|
||||
public function getColor() {
|
||||
return 'red';
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return pht(
|
||||
'%s deleted this file.',
|
||||
$this->renderAuthor());
|
||||
}
|
||||
|
||||
public function getTitleForFeed() {
|
||||
return pht(
|
||||
'%s deleted %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderObject());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplicationApplicationTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
public function getTemplateApplicationTransaction() {
|
||||
return new PhabricatorApplicationApplicationTransaction();
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplicationApplicationTransaction
|
||||
extends PhabricatorModularTransaction {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'application';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return PhabricatorApplicationApplicationPHIDType::TYPECONST;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return new PhabricatorApplicationTransactionComment();
|
||||
}
|
||||
|
||||
public function getBaseTransactionClass() {
|
||||
return 'PhabricatorApplicationTransactionType';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorApplicationSchemaSpec
|
||||
extends PhabricatorConfigSchemaSpec {
|
||||
|
||||
public function buildSchemata() {
|
||||
$this->buildEdgeSchemata(new PhabricatorApplicationsApplication());
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,4 @@
|
|||
<?php
|
||||
|
||||
abstract class PhabricatorApplicationTransactionType
|
||||
extends PhabricatorModularTransactionType {}
|
|
@ -37,6 +37,15 @@ final class PhabricatorMailManagementShowOutboundWorkflow
|
|||
'--id'));
|
||||
}
|
||||
|
||||
foreach ($ids as $id) {
|
||||
if (!ctype_digit($id)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Argument "%s" is not a valid message ID.',
|
||||
$id));
|
||||
}
|
||||
}
|
||||
|
||||
$messages = id(new PhabricatorMetaMTAMail())->loadAllWhere(
|
||||
'id IN (%Ld)',
|
||||
$ids);
|
||||
|
|
|
@ -18,6 +18,11 @@ final class PhabricatorNotificationClient extends Phobject {
|
|||
}
|
||||
|
||||
public static function tryToPostMessage(array $data) {
|
||||
$unique_id = Filesystem::readRandomCharacters(32);
|
||||
$data = $data + array(
|
||||
'uniqueID' => $unique_id,
|
||||
);
|
||||
|
||||
$servers = PhabricatorNotificationServerRef::getEnabledAdminServers();
|
||||
|
||||
shuffle($servers);
|
||||
|
|
|
@ -47,6 +47,7 @@ final class PhabricatorNotificationIndividualController
|
|||
'title' => $data['title'],
|
||||
'body' => $data['body'],
|
||||
'content' => hsprintf('%s', $content),
|
||||
'uniqueID' => 'story/'.$story->getChronologicalKey(),
|
||||
);
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent($response);
|
||||
|
|
|
@ -4,7 +4,7 @@ abstract class PhabricatorPackagesEditor
|
|||
extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getEditorApplicationClass() {
|
||||
return 'PhabricatorPasteApplication';
|
||||
return 'PhabricatorPackagesApplication';
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
|
|
|
@ -28,10 +28,9 @@ final class PhabricatorUserMessageCountCacheType
|
|||
|
||||
$user_phids = mpull($users, 'getPHID');
|
||||
|
||||
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
||||
$unread = id(new ConpherenceParticipantCountQuery())
|
||||
->withParticipantPHIDs($user_phids)
|
||||
->withParticipationStatus($unread_status)
|
||||
->withUnread(true)
|
||||
->execute();
|
||||
|
||||
$empty = array_fill_keys($user_phids, 0);
|
||||
|
|
|
@ -48,8 +48,6 @@ final class PhabricatorPhurlApplication extends PhabricatorApplication {
|
|||
'url/' => array(
|
||||
$this->getEditRoutePattern('edit/')
|
||||
=> 'PhabricatorPhurlURLEditController',
|
||||
'comment/(?P<id>[1-9]\d*)/'
|
||||
=> 'PhabricatorPhurlURLCommentController',
|
||||
),
|
||||
),
|
||||
);
|
||||
|
|
|
@ -1,63 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorPhurlURLCommentController
|
||||
extends PhabricatorPhurlController {
|
||||
|
||||
public function handleRequest(AphrontRequest $request) {
|
||||
if (!$request->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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -30,6 +30,7 @@ final class PhabricatorPhurlURLViewController
|
|||
$timeline = $this->buildTransactionTimeline(
|
||||
$url,
|
||||
new PhabricatorPhurlURLTransactionQuery());
|
||||
$timeline->setQuoteRef($url->getMonogram());
|
||||
|
||||
$header = $this->buildHeaderView($url);
|
||||
$curtain = $this->buildCurtain($url);
|
||||
|
@ -39,20 +40,7 @@ final class PhabricatorPhurlURLViewController
|
|||
->setErrors(array(pht('This URL is invalid due to a bad protocol.')))
|
||||
->setIsHidden($url->isValid());
|
||||
|
||||
$is_serious = PhabricatorEnv::getEnvConfig('phabricator.serious-business');
|
||||
$add_comment_header = $is_serious
|
||||
? pht('Add Comment')
|
||||
: pht('More Cowbell');
|
||||
$draft = PhabricatorDraft::newFromUserAndKey($viewer, $url->getPHID());
|
||||
$comment_uri = $this->getApplicationURI(
|
||||
'/url/comment/'.$url->getID().'/');
|
||||
$add_comment_form = id(new PhabricatorApplicationTransactionCommentView())
|
||||
->setUser($viewer)
|
||||
->setObjectPHID($url->getPHID())
|
||||
->setDraft($draft)
|
||||
->setHeaderText($add_comment_header)
|
||||
->setAction($comment_uri)
|
||||
->setSubmitButtonName(pht('Add Comment'));
|
||||
$add_comment_form = $this->buildCommentForm($url, $timeline);
|
||||
|
||||
$view = id(new PHUITwoColumnView())
|
||||
->setHeader($header)
|
||||
|
@ -72,7 +60,16 @@ final class PhabricatorPhurlURLViewController
|
|||
array(
|
||||
$view,
|
||||
));
|
||||
}
|
||||
|
||||
private function buildCommentForm(PhabricatorPhurlURL $url, $timeline) {
|
||||
$viewer = $this->getViewer();
|
||||
$box = id(new PhabricatorPhurlURLEditEngine())
|
||||
->setViewer($viewer)
|
||||
->buildEditEngineCommentView($url)
|
||||
->setTransactionTimeline($timeline);
|
||||
|
||||
return $box;
|
||||
}
|
||||
|
||||
private function buildHeaderView(PhabricatorPhurlURL $url) {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
interface PhabricatorFulltextInterface {
|
||||
interface PhabricatorFulltextInterface
|
||||
extends PhabricatorIndexableInterface {
|
||||
|
||||
public function newFulltextEngine();
|
||||
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
<?php
|
||||
|
||||
interface PhabricatorIndexableInterface {}
|
|
@ -1,6 +1,7 @@
|
|||
<?php
|
||||
|
||||
interface PhabricatorNgramsInterface {
|
||||
interface PhabricatorNgramsInterface
|
||||
extends PhabricatorIndexableInterface {
|
||||
|
||||
public function newNgrams();
|
||||
|
||||
|
|
|
@ -213,7 +213,7 @@ final class PhabricatorSearchManagementIndexWorkflow
|
|||
|
||||
private function loadPHIDsByTypes($type) {
|
||||
$objects = id(new PhutilClassMapQuery())
|
||||
->setAncestorClass('PhabricatorFulltextInterface')
|
||||
->setAncestorClass('PhabricatorIndexableInterface')
|
||||
->execute();
|
||||
|
||||
$normalized_type = phutil_utf8_strtolower($type);
|
||||
|
|
|
@ -0,0 +1,81 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorConpherenceSoundSetting
|
||||
extends PhabricatorSelectSetting {
|
||||
|
||||
const SETTINGKEY = 'conpherence-sound';
|
||||
|
||||
const VALUE_CONPHERENCE_SILENT = '0';
|
||||
const VALUE_CONPHERENCE_MENTION = '1';
|
||||
const VALUE_CONPHERENCE_ALL = '2';
|
||||
|
||||
public function getSettingName() {
|
||||
return pht('Conpherence Sound');
|
||||
}
|
||||
|
||||
public function getSettingPanelKey() {
|
||||
return PhabricatorConpherencePreferencesSettingsPanel::PANELKEY;
|
||||
}
|
||||
|
||||
protected function getControlInstructions() {
|
||||
return pht(
|
||||
'Choose the default sound behavior for new Conpherence rooms.');
|
||||
}
|
||||
|
||||
protected function isEnabledForViewer(PhabricatorUser $viewer) {
|
||||
return PhabricatorApplication::isClassInstalledForViewer(
|
||||
'PhabricatorConpherenceApplication',
|
||||
$viewer);
|
||||
}
|
||||
|
||||
public function getSettingDefaultValue() {
|
||||
return self::VALUE_CONPHERENCE_ALL;
|
||||
}
|
||||
|
||||
protected function getSelectOptions() {
|
||||
return self::getOptionsMap();
|
||||
}
|
||||
|
||||
public static function getSettingLabel($key) {
|
||||
$labels = self::getOptionsMap();
|
||||
return idx($labels, $key, pht('Unknown ("%s")', $key));
|
||||
}
|
||||
|
||||
public static function getDefaultSound($value) {
|
||||
switch ($value) {
|
||||
case self::VALUE_CONPHERENCE_ALL:
|
||||
return array(
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE =>
|
||||
ConpherenceRoomSettings::DEFAULT_RECEIVE_SOUND,
|
||||
ConpherenceRoomSettings::SOUND_MENTION =>
|
||||
ConpherenceRoomSettings::DEFAULT_MENTION_SOUND,
|
||||
);
|
||||
break;
|
||||
case self::VALUE_CONPHERENCE_MENTION:
|
||||
return array(
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE =>
|
||||
ConpherenceRoomSettings::DEFAULT_NO_SOUND,
|
||||
ConpherenceRoomSettings::SOUND_MENTION =>
|
||||
ConpherenceRoomSettings::DEFAULT_MENTION_SOUND,
|
||||
);
|
||||
break;
|
||||
case self::VALUE_CONPHERENCE_SILENT:
|
||||
return array(
|
||||
ConpherenceRoomSettings::SOUND_RECEIVE =>
|
||||
ConpherenceRoomSettings::DEFAULT_NO_SOUND,
|
||||
ConpherenceRoomSettings::SOUND_MENTION =>
|
||||
ConpherenceRoomSettings::DEFAULT_NO_SOUND,
|
||||
);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private static function getOptionsMap() {
|
||||
return array(
|
||||
self::VALUE_CONPHERENCE_SILENT => pht('No Sounds'),
|
||||
// self::VALUE_CONPHERENCE_MENTION => pht('Mentions Only'),
|
||||
self::VALUE_CONPHERENCE_ALL => pht('All Messages'),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -1801,7 +1801,10 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
$old = array_fuse($xaction->getOldValue());
|
||||
}
|
||||
|
||||
$new = $xaction->getNewValue();
|
||||
return $this->getPHIDList($old, $xaction->getNewValue());
|
||||
}
|
||||
|
||||
public function getPHIDList(array $old, array $new) {
|
||||
$new_add = idx($new, '+', array());
|
||||
unset($new['+']);
|
||||
$new_rem = idx($new, '-', array());
|
||||
|
|
|
@ -309,4 +309,10 @@ abstract class PhabricatorModularTransactionType
|
|||
return $this->getStorage()->getIsCreateTransaction();
|
||||
}
|
||||
|
||||
final protected function getPHIDList(array $old, array $new) {
|
||||
$editor = $this->getEditor();
|
||||
|
||||
return $editor->getPHIDList($old, $new);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -10,7 +10,7 @@ Database System
|
|||
Phabricator uses MySQL or another MySQL-compatible database (like MariaDB
|
||||
or Amazon RDS).
|
||||
|
||||
Phabricator the InnoDB table engine. The only exception is the
|
||||
Phabricator uses the InnoDB table engine. The only exception is the
|
||||
`search_documentfield` table which uses MyISAM because MySQL doesn't support
|
||||
fulltext search in InnoDB (recent versions do, but we haven't added support
|
||||
yet).
|
||||
|
@ -102,7 +102,7 @@ An example of such usage can be found in column
|
|||
Primary Keys
|
||||
============
|
||||
|
||||
Most tables have auto-increment column named `id`. Adding an ID column is
|
||||
Most tables have an auto-increment column named `id`. Adding an ID column is
|
||||
appropriate for most tables (even tables that have another natural unique key),
|
||||
as it improves consistency and makes it easier to perform generic operations
|
||||
on objects.
|
||||
|
@ -134,12 +134,12 @@ eventually, but there isn't a strong case for them at the present time.
|
|||
PHIDs
|
||||
=====
|
||||
|
||||
Each globally referencable object in Phabricator has its associated PHID
|
||||
Each globally referencable object in Phabricator has an associated PHID
|
||||
("Phabricator ID") which serves as a global identifier, similar to a GUID.
|
||||
We use PHIDs for referencing data in different databases.
|
||||
|
||||
We use both autoincrementing IDs and global PHIDs because each is useful in
|
||||
different contexts. Autoincrementing IDs are meaningfully ordered and allow
|
||||
We use both auto-incrementing IDs and global PHIDs because each is useful in
|
||||
different contexts. Auto-incrementing IDs are meaningfully ordered and allow
|
||||
us to construct short, human-readable object names (like `D2258`) and URIs.
|
||||
Global PHIDs allow us to represent relationships between different types of
|
||||
objects in a homogeneous way.
|
||||
|
@ -154,7 +154,7 @@ Transactions
|
|||
============
|
||||
|
||||
Transactional code should be written using transactions. Example of such code is
|
||||
inserting multiple records where one doesn't make sense without the other or
|
||||
inserting multiple records where one doesn't make sense without the other, or
|
||||
selecting data later used for update. See chapter in @{class:LiskDAO}.
|
||||
|
||||
Advanced Features
|
||||
|
@ -195,12 +195,12 @@ set names:
|
|||
|
||||
| Variable | Meaning | Notes |
|
||||
|---|---|---|
|
||||
| {$NAMESPACE} | Storage Namespace | Defaults to `phabricator` |
|
||||
| {$CHARSET} | Default Charset | Mostly used to specify table charset |
|
||||
| {$COLLATE_TEXT} | Text Collation | For most text (case-sensitive) |
|
||||
| {$COLLATE_SORT} | Sort Collation | For sortable text (case-insensitive) |
|
||||
| {$CHARSET_FULLTEXT} | Fulltext Charset | Specify explicitly for fulltext |
|
||||
| {$COLLATE_FULLTEXT} | Fulltext Collate | Specify explicitly for fulltext |
|
||||
| `{$NAMESPACE}` | Storage Namespace | Defaults to `phabricator` |
|
||||
| `{$CHARSET}` | Default Charset | Mostly used to specify table charset |
|
||||
| `{$COLLATE_TEXT}` | Text Collation | For most text (case-sensitive) |
|
||||
| `{$COLLATE_SORT}` | Sort Collation | For sortable text (case-insensitive) |
|
||||
| `{$CHARSET_FULLTEXT}` | Fulltext Charset | Specify explicitly for fulltext |
|
||||
| `{$COLLATE_FULLTEXT}` | Fulltext Collate | Specify explicitly for fulltext |
|
||||
|
||||
|
||||
**Test your patch**. Run `bin/storage upgrade` to test your patch.
|
||||
|
|
|
@ -52,9 +52,9 @@ problems.
|
|||
I joined the new Dev Tools team around February 2010 and took over Diffcamp. I
|
||||
renamed it to Differential, moved it to a new Alite-based infrastructure with
|
||||
Javelin, and started making it somewhat less terrible. I eventually wrote
|
||||
Diffusion and build Herald to replace a very difficult-to-use predecessor. These
|
||||
Diffusion and built Herald to replace a very difficult-to-use predecessor. These
|
||||
tools were less negatively received than the older versions. By December 2010 I
|
||||
started open sourcing them; Haste became //Celerity// and Alite became
|
||||
//Aphront//. I wrote Maniphest to track open issues with the project in January
|
||||
or February and we open sourced Phabricator in late April, shortly after I left
|
||||
Facebook.
|
||||
or February, left Facebook in April, and shortly after, we open sourced
|
||||
Phabricator.
|
||||
|
|
|
@ -134,5 +134,5 @@ query escaping system the rest of the application does.
|
|||
|
||||
Hopefully, whatever language you're writing in has good query libraries that
|
||||
can handle escaping for you. If so, use them. If you're using PHP and don't have
|
||||
a solution in place yet, the Phabricator implementation of qsprintf() is similar
|
||||
to Facebook's system and was successful there.
|
||||
a solution in place yet, the Phabricator implementation of `qsprintf()` is
|
||||
similar to Facebook's system and was successful there.
|
||||
|
|
|
@ -1,87 +0,0 @@
|
|||
@title Chat Bot Technical Documentation
|
||||
@group bot
|
||||
|
||||
Configuring and extending the chat bot.
|
||||
|
||||
= Overview =
|
||||
|
||||
Phabricator includes a simple chat bot daemon, which is primarily intended as
|
||||
an example of how you can write an external script that interfaces with
|
||||
Phabricator over Conduit and does some kind of useful work. If you use IRC or
|
||||
another supported chat protocol, you can also have the bot hang out in your
|
||||
channel.
|
||||
|
||||
NOTE: The chat bot is somewhat experimental and not very mature.
|
||||
|
||||
= Configuring the Bot =
|
||||
|
||||
The bot reads a JSON configuration file. You can find an example in:
|
||||
|
||||
resources/chatbot/example_config.json
|
||||
|
||||
These are the configuration values it reads:
|
||||
|
||||
- `server` String, required, the server to connect to.
|
||||
- `port` Int, optional, the port to connect to (defaults to 6667).
|
||||
- `ssl` Bool, optional, whether to connect via SSL or not (defaults to
|
||||
false).
|
||||
- `nick` String, nickname to use.
|
||||
- `user` String, optional, username to use (defaults to `nick`).
|
||||
- `pass` String, optional, password for server.
|
||||
- `nickpass` String, optional, password for NickServ.
|
||||
- `join` Array, list of channels to join.
|
||||
- `handlers` Array, list of handlers to run. These are like plugins for the
|
||||
bot.
|
||||
- `conduit.uri`, `conduit.token` Conduit configuration,
|
||||
see below.
|
||||
- `notification.channels` Notification configuration, see below.
|
||||
|
||||
= Handlers =
|
||||
|
||||
You specify a list of "handlers", which are basically plugins or modules for
|
||||
the bot. These are the default handlers available:
|
||||
|
||||
- @{class:PhabricatorBotObjectNameHandler} This handler looks for users
|
||||
mentioning Phabricator objects like "T123" and "D345" in chat, looks them
|
||||
up, and says their name with a link to the object. Requires conduit.
|
||||
- @{class:PhabricatorBotFeedNotificationHandler} This handler posts
|
||||
notifications about changes to revisions to the channels listed in
|
||||
`notification.channels`.
|
||||
- @{class:PhabricatorBotLogHandler} This handler records chatlogs which can
|
||||
be browsed in the Phabricator web interface.
|
||||
- @{class:PhabricatorBotSymbolHandler} This handler posts responses to lookups
|
||||
for symbols in Diffusion
|
||||
- @{class:PhabricatorBotMacroHandler} This handler looks for users mentioning
|
||||
macros, if found will convert image to ASCII and output in chat. Configure
|
||||
with `macro.size` and `macro.aspect`
|
||||
|
||||
You can also write your own handlers, by extending
|
||||
@{class:PhabricatorBotHandler}.
|
||||
|
||||
= Conduit =
|
||||
|
||||
Some handlers (e.g., @{class:PhabricatorBotObjectNameHandler}) need to read data
|
||||
from Phabricator over Conduit, Phabricator's HTTP API. You can use this method
|
||||
to allow other scripts or programs to access Phabricator's data from different
|
||||
servers and in different languages.
|
||||
|
||||
To allow the bot to access Conduit, you need to create a user that it can login
|
||||
with. To do this, login to Phabricator as an administrator and go to
|
||||
`People -> Create New Account`. Create a new account and flag them as a
|
||||
"Bot/Script". Then in your configuration file, set these parameters:
|
||||
|
||||
- `conduit.uri` The URI for your Phabricator install, like
|
||||
`http://phabricator.example.com/`
|
||||
- `conduit.token` The user's conduit API token, from the "Conduit API Tokens"
|
||||
tab in the user's administrative view.
|
||||
|
||||
Now the bot should be able to connect to Phabricator via Conduit.
|
||||
|
||||
= Starting the Bot =
|
||||
|
||||
The bot is a Phabricator daemon, so start it with `phd`:
|
||||
|
||||
./bin/phd launch phabricatorbot <absolute_path_to_config_file>
|
||||
|
||||
If you have issues you can try `debug` instead of `launch`, see
|
||||
@{article:Managing Daemons with phd} for more information.
|
|
@ -1,170 +0,0 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Simple IRC bot which runs as a Phabricator daemon. Although this bot is
|
||||
* somewhat useful, it is also intended to serve as a demo of how to write
|
||||
* "system agents" which communicate with Phabricator over Conduit, so you can
|
||||
* script system interactions and integrate with other systems.
|
||||
*
|
||||
* NOTE: This is super janky and experimental right now.
|
||||
*/
|
||||
final class PhabricatorBot extends PhabricatorDaemon {
|
||||
|
||||
private $handlers;
|
||||
|
||||
private $conduit;
|
||||
private $config;
|
||||
private $pollFrequency;
|
||||
private $protocolAdapter;
|
||||
|
||||
protected function run() {
|
||||
$argv = $this->getArgv();
|
||||
if (count($argv) !== 1) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Usage: %s %s',
|
||||
__CLASS__,
|
||||
'<json_config_file>'));
|
||||
}
|
||||
|
||||
$json_raw = Filesystem::readFile($argv[0]);
|
||||
try {
|
||||
$config = phutil_json_decode($json_raw);
|
||||
} catch (PhutilJSONParserException $ex) {
|
||||
throw new PhutilProxyException(
|
||||
pht("File '%s' is not valid JSON!", $argv[0]),
|
||||
$ex);
|
||||
}
|
||||
|
||||
$nick = idx($config, 'nick', 'phabot');
|
||||
$handlers = idx($config, 'handlers', array());
|
||||
$protocol_adapter_class = idx(
|
||||
$config,
|
||||
'protocol-adapter',
|
||||
'PhabricatorIRCProtocolAdapter');
|
||||
$this->pollFrequency = idx($config, 'poll-frequency', 1);
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
foreach ($handlers as $handler) {
|
||||
$obj = newv($handler, array($this));
|
||||
$this->handlers[] = $obj;
|
||||
}
|
||||
|
||||
$ca_bundle = idx($config, 'https.cabundle');
|
||||
if ($ca_bundle) {
|
||||
HTTPSFuture::setGlobalCABundleFromPath($ca_bundle);
|
||||
}
|
||||
|
||||
$conduit_uri = idx($config, 'conduit.uri');
|
||||
if ($conduit_uri) {
|
||||
$conduit_token = idx($config, 'conduit.token');
|
||||
|
||||
// Normalize the path component of the URI so users can enter the
|
||||
// domain without the "/api/" part.
|
||||
$conduit_uri = new PhutilURI($conduit_uri);
|
||||
|
||||
$conduit_host = (string)$conduit_uri->setPath('/');
|
||||
$conduit_uri = (string)$conduit_uri->setPath('/api/');
|
||||
|
||||
$conduit = new ConduitClient($conduit_uri);
|
||||
if ($conduit_token) {
|
||||
$conduit->setConduitToken($conduit_token);
|
||||
} else {
|
||||
$conduit_user = idx($config, 'conduit.user');
|
||||
$conduit_cert = idx($config, 'conduit.cert');
|
||||
|
||||
$response = $conduit->callMethodSynchronous(
|
||||
'conduit.connect',
|
||||
array(
|
||||
'client' => __CLASS__,
|
||||
'clientVersion' => '1.0',
|
||||
'clientDescription' => php_uname('n').':'.$nick,
|
||||
'host' => $conduit_host,
|
||||
'user' => $conduit_user,
|
||||
'certificate' => $conduit_cert,
|
||||
));
|
||||
}
|
||||
|
||||
$this->conduit = $conduit;
|
||||
}
|
||||
|
||||
// Instantiate Protocol Adapter, for now follow same technique as
|
||||
// handler instantiation
|
||||
$this->protocolAdapter = newv($protocol_adapter_class, array());
|
||||
$this->protocolAdapter
|
||||
->setConfig($this->config)
|
||||
->connect();
|
||||
|
||||
$this->runLoop();
|
||||
|
||||
$this->protocolAdapter->disconnect();
|
||||
}
|
||||
|
||||
public function getConfig($key, $default = null) {
|
||||
return idx($this->config, $key, $default);
|
||||
}
|
||||
|
||||
private function runLoop() {
|
||||
do {
|
||||
PhabricatorCaches::destroyRequestCache();
|
||||
|
||||
$this->stillWorking();
|
||||
|
||||
$messages = $this->protocolAdapter->getNextMessages($this->pollFrequency);
|
||||
if (count($messages) > 0) {
|
||||
foreach ($messages as $message) {
|
||||
$this->routeMessage($message);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->handlers as $handler) {
|
||||
$handler->runBackgroundTasks();
|
||||
}
|
||||
} while (!$this->shouldExit());
|
||||
|
||||
}
|
||||
|
||||
public function writeMessage(PhabricatorBotMessage $message) {
|
||||
return $this->protocolAdapter->writeMessage($message);
|
||||
}
|
||||
|
||||
private function routeMessage(PhabricatorBotMessage $message) {
|
||||
$ignore = $this->getConfig('ignore');
|
||||
if ($ignore) {
|
||||
$sender = $message->getSender();
|
||||
if ($sender && in_array($sender->getName(), $ignore)) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ($message->getCommand() == 'LOG') {
|
||||
$this->log('[LOG] '.$message->getBody());
|
||||
}
|
||||
|
||||
foreach ($this->handlers as $handler) {
|
||||
try {
|
||||
$handler->receiveMessage($message);
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getAdapter() {
|
||||
return $this->protocolAdapter;
|
||||
}
|
||||
|
||||
public function getConduit() {
|
||||
if (empty($this->conduit)) {
|
||||
throw new Exception(
|
||||
pht(
|
||||
"This bot is not configured with a Conduit uplink. Set '%s' and ".
|
||||
"'%s' in the configuration to connect.",
|
||||
'conduit.uri',
|
||||
'conduit.token'));
|
||||
}
|
||||
return $this->conduit;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,52 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorBotMessage extends Phobject {
|
||||
|
||||
private $sender;
|
||||
private $command;
|
||||
private $body;
|
||||
private $target;
|
||||
private $public;
|
||||
|
||||
public function __construct() {
|
||||
// By default messages are public
|
||||
$this->public = true;
|
||||
}
|
||||
|
||||
public function setSender(PhabricatorBotTarget $sender = null) {
|
||||
$this->sender = $sender;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getSender() {
|
||||
return $this->sender;
|
||||
}
|
||||
|
||||
public function setCommand($command) {
|
||||
$this->command = $command;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommand() {
|
||||
return $this->command;
|
||||
}
|
||||
|
||||
public function setBody($body) {
|
||||
$this->body = $body;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getBody() {
|
||||
return $this->body;
|
||||
}
|
||||
|
||||
public function setTarget(PhabricatorBotTarget $target = null) {
|
||||
$this->target = $target;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTarget() {
|
||||
return $this->target;
|
||||
}
|
||||
|
||||
}
|
|
@ -1,93 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorBotFlowdockProtocolAdapter
|
||||
extends PhabricatorStreamingProtocolAdapter {
|
||||
|
||||
public function getServiceType() {
|
||||
return 'Flowdock';
|
||||
}
|
||||
|
||||
protected function buildStreamingUrl($channel) {
|
||||
$organization = $this->getConfig('flowdock.organization');
|
||||
if (empty($organization)) {
|
||||
$this->getConfig('organization');
|
||||
}
|
||||
if (empty($organization)) {
|
||||
throw new Exception(
|
||||
'"flowdock.organization" configuration variable not set');
|
||||
}
|
||||
|
||||
|
||||
$ssl = $this->getConfig('ssl');
|
||||
|
||||
$url = ($ssl) ? 'https://' : 'http://';
|
||||
$url .= "{$this->authtoken}@stream.flowdock.com";
|
||||
$url .= "/flows/{$organization}/{$channel}";
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function processMessage(array $m_obj) {
|
||||
$command = null;
|
||||
switch ($m_obj['event']) {
|
||||
case 'message':
|
||||
$command = 'MESSAGE';
|
||||
break;
|
||||
default:
|
||||
// For now, ignore anything which we don't otherwise know about.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($command === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: These should be usernames, not user IDs.
|
||||
$sender = id(new PhabricatorBotUser())
|
||||
->setName($m_obj['user']);
|
||||
|
||||
$target = id(new PhabricatorBotChannel())
|
||||
->setName($m_obj['flow']);
|
||||
|
||||
return id(new PhabricatorBotMessage())
|
||||
->setCommand($command)
|
||||
->setSender($sender)
|
||||
->setTarget($target)
|
||||
->setBody($m_obj['content']);
|
||||
}
|
||||
|
||||
public function writeMessage(PhabricatorBotMessage $message) {
|
||||
switch ($message->getCommand()) {
|
||||
case 'MESSAGE':
|
||||
$this->speak(
|
||||
$message->getBody(),
|
||||
$message->getTarget());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function speak(
|
||||
$body,
|
||||
PhabricatorBotTarget $flow) {
|
||||
// The $flow->getName() returns the flow's UUID,
|
||||
// as such, the Flowdock API does not require the organization
|
||||
// to be specified in the URI
|
||||
$this->performPost(
|
||||
'/messages',
|
||||
array(
|
||||
'flow' => $flow->getName(),
|
||||
'event' => 'message',
|
||||
'content' => $body,
|
||||
));
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
if ($this->readHandles) {
|
||||
foreach ($this->readHandles as $read_handle) {
|
||||
curl_multi_remove_handle($this->multiHandle, $read_handle);
|
||||
curl_close($read_handle);
|
||||
}
|
||||
}
|
||||
|
||||
curl_multi_close($this->multiHandle);
|
||||
}
|
||||
}
|
|
@ -1,114 +0,0 @@
|
|||
<?php
|
||||
|
||||
final class PhabricatorCampfireProtocolAdapter
|
||||
extends PhabricatorStreamingProtocolAdapter {
|
||||
|
||||
public function getServiceType() {
|
||||
return 'Campfire';
|
||||
}
|
||||
|
||||
protected function buildStreamingUrl($channel) {
|
||||
$ssl = $this->getConfig('ssl');
|
||||
|
||||
$url = ($ssl) ? 'https://' : 'http://';
|
||||
$url .= "streaming.campfirenow.com/room/{$channel}/live.json";
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
protected function processMessage(array $m_obj) {
|
||||
$command = null;
|
||||
switch ($m_obj['type']) {
|
||||
case 'TextMessage':
|
||||
$command = 'MESSAGE';
|
||||
break;
|
||||
case 'PasteMessage':
|
||||
$command = 'PASTE';
|
||||
break;
|
||||
default:
|
||||
// For now, ignore anything which we don't otherwise know about.
|
||||
break;
|
||||
}
|
||||
|
||||
if ($command === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: These should be usernames, not user IDs.
|
||||
$sender = id(new PhabricatorBotUser())
|
||||
->setName($m_obj['user_id']);
|
||||
|
||||
$target = id(new PhabricatorBotChannel())
|
||||
->setName($m_obj['room_id']);
|
||||
|
||||
return id(new PhabricatorBotMessage())
|
||||
->setCommand($command)
|
||||
->setSender($sender)
|
||||
->setTarget($target)
|
||||
->setBody($m_obj['body']);
|
||||
}
|
||||
|
||||
public function writeMessage(PhabricatorBotMessage $message) {
|
||||
switch ($message->getCommand()) {
|
||||
case 'MESSAGE':
|
||||
$this->speak(
|
||||
$message->getBody(),
|
||||
$message->getTarget());
|
||||
break;
|
||||
case 'SOUND':
|
||||
$this->speak(
|
||||
$message->getBody(),
|
||||
$message->getTarget(),
|
||||
'SoundMessage');
|
||||
break;
|
||||
case 'PASTE':
|
||||
$this->speak(
|
||||
$message->getBody(),
|
||||
$message->getTarget(),
|
||||
'PasteMessage');
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function joinRoom($room_id) {
|
||||
$this->performPost("/room/{$room_id}/join.json");
|
||||
$this->inRooms[$room_id] = true;
|
||||
}
|
||||
|
||||
private function leaveRoom($room_id) {
|
||||
$this->performPost("/room/{$room_id}/leave.json");
|
||||
unset($this->inRooms[$room_id]);
|
||||
}
|
||||
|
||||
private function speak(
|
||||
$message,
|
||||
PhabricatorBotTarget $channel,
|
||||
$type = 'TextMessage') {
|
||||
|
||||
$room_id = $channel->getName();
|
||||
|
||||
$this->performPost(
|
||||
"/room/{$room_id}/speak.json",
|
||||
array(
|
||||
'message' => array(
|
||||
'type' => $type,
|
||||
'body' => $message,
|
||||
),
|
||||
));
|
||||
}
|
||||
|
||||
public function __destruct() {
|
||||
foreach ($this->inRooms as $room_id => $ignored) {
|
||||
$this->leaveRoom($room_id);
|
||||
}
|
||||
|
||||
if ($this->readHandles) {
|
||||
foreach ($this->readHandles as $read_handle) {
|
||||
curl_multi_remove_handle($this->multiHandle, $read_handle);
|
||||
curl_close($read_handle);
|
||||
}
|
||||
}
|
||||
|
||||
curl_multi_close($this->multiHandle);
|
||||
}
|
||||
}
|