mirror of
https://we.phorge.it/source/phorge.git
synced 2025-02-19 18:28:39 +01:00
Conpherence V1
Summary: it's ugly. but it works. basically. See T2399 for a roughly prioritized list of what still needs to happen. Test Plan: - created a conpherence with myself from my profile - created a conpherence with myself from "new conpherence" - created a conphernece with another from "new conpherence" - created a conpherence with several others - created a conpherence with files in the initial post - verified files via comment text ("{F232} is awesome!") and via traditional attach - edited a conpherence image - verified it showed up in the header and in the conpherence menu on the left - edited a conpherence title - verified it showed up in the header and in the conpherence menu on the right - verified each widget showed up when clicked and displayed the proper data - calendar being an exception since it sucks so hard right now. Reviewers: epriestley, chad Reviewed By: epriestley CC: aran, epriestley, chad, codeblock, Korvin Maniphest Tasks: T2301 Differential Revision: https://secure.phabricator.com/D4620
This commit is contained in:
parent
72734318e7
commit
b873f3f991
44 changed files with 3054 additions and 51 deletions
83
resources/sql/patches/20130111.conpherence.sql
Normal file
83
resources/sql/patches/20130111.conpherence.sql
Normal file
|
@ -0,0 +1,83 @@
|
|||
|
||||
CREATE TABLE {$NAMESPACE}_conpherence.conpherence_thread (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
title VARCHAR(255),
|
||||
imagePHID VARCHAR(64) COLLATE utf8_bin,
|
||||
mailKey VARCHAR(20) NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY(phid)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_conpherence.conpherence_participant (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
participantPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
conpherencePHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
participationStatus INT UNSIGNED NOT NULL DEFAULT 0,
|
||||
dateTouched INT UNSIGNED NOT NULL,
|
||||
behindTransactionPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY(conpherencePHID, participantPHID),
|
||||
KEY(participantPHID, participationStatus, dateTouched)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_conpherence.edge (
|
||||
src varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
||||
type varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
||||
dst varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
||||
dateCreated int(10) unsigned NOT NULL,
|
||||
seq int(10) unsigned NOT NULL,
|
||||
dataID int(10) unsigned DEFAULT NULL,
|
||||
PRIMARY KEY (src, type, dst),
|
||||
KEY src (src, type, dateCreated, seq)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_conpherence.edgedata (
|
||||
id int(10) unsigned NOT NULL AUTO_INCREMENT,
|
||||
data longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_conpherence.conpherence_transaction (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
commentPHID VARCHAR(64) COLLATE utf8_bin,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin,
|
||||
oldValue LONGTEXT NOT NULL COLLATE utf8_bin,
|
||||
newValue LONGTEXT NOT NULL COLLATE utf8_bin,
|
||||
contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
KEY `key_object` (objectPHID)
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
||||
CREATE TABLE {$NAMESPACE}_conpherence.conpherence_transaction_comment (
|
||||
id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT,
|
||||
phid VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
transactionPHID VARCHAR(64) COLLATE utf8_bin,
|
||||
authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin,
|
||||
commentVersion INT UNSIGNED NOT NULL,
|
||||
content LONGTEXT NOT NULL COLLATE utf8_bin,
|
||||
contentSource LONGTEXT NOT NULL COLLATE utf8_bin,
|
||||
isDeleted BOOL NOT NULL,
|
||||
dateCreated INT UNSIGNED NOT NULL,
|
||||
dateModified INT UNSIGNED NOT NULL,
|
||||
|
||||
conpherencePHID VARCHAR(64) COLLATE utf8_bin,
|
||||
|
||||
UNIQUE KEY `key_phid` (phid),
|
||||
UNIQUE KEY `key_version` (transactionPHID, commentVersion),
|
||||
UNIQUE KEY `key_draft` (authorPHID, conpherencePHID, transactionPHID)
|
||||
|
||||
) ENGINE=InnoDB, COLLATE utf8_general_ci;
|
||||
|
|
@ -618,7 +618,7 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'aphront-form-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/45162273/rsrc/css/aphront/form-view.css',
|
||||
'uri' => '/res/1e191b83/rsrc/css/aphront/form-view.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
|
@ -725,6 +725,51 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/css/application/config/config-options.css',
|
||||
),
|
||||
'conpherence-header-pane-css' =>
|
||||
array(
|
||||
'uri' => '/res/5a02bdbe/rsrc/css/application/conpherence/header-pane.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/application/conpherence/header-pane.css',
|
||||
),
|
||||
'conpherence-menu-css' =>
|
||||
array(
|
||||
'uri' => '/res/b893e529/rsrc/css/application/conpherence/menu.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/application/conpherence/menu.css',
|
||||
),
|
||||
'conpherence-message-pane-css' =>
|
||||
array(
|
||||
'uri' => '/res/32073bd5/rsrc/css/application/conpherence/message-pane.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/application/conpherence/message-pane.css',
|
||||
),
|
||||
'conpherence-update-css' =>
|
||||
array(
|
||||
'uri' => '/res/8e4757b5/rsrc/css/application/conpherence/update.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/application/conpherence/update.css',
|
||||
),
|
||||
'conpherence-widget-pane-css' =>
|
||||
array(
|
||||
'uri' => '/res/7af40cac/rsrc/css/application/conpherence/widget-pane.css',
|
||||
'type' => 'css',
|
||||
'requires' =>
|
||||
array(
|
||||
),
|
||||
'disk' => '/rsrc/css/application/conpherence/widget-pane.css',
|
||||
),
|
||||
'differential-changeset-view-css' =>
|
||||
array(
|
||||
'uri' => '/res/ea694162/rsrc/css/application/differential/changeset-view.css',
|
||||
|
@ -1039,6 +1084,45 @@ celerity_register_resource_map(array(
|
|||
),
|
||||
'disk' => '/rsrc/js/application/diffusion/behavior-audit-preview.js',
|
||||
),
|
||||
'javelin-behavior-conpherence-init' =>
|
||||
array(
|
||||
'uri' => '/res/bd911b43/rsrc/js/application/conpherence/behavior-init.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-stratcom',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/conpherence/behavior-init.js',
|
||||
),
|
||||
'javelin-behavior-conpherence-menu' =>
|
||||
array(
|
||||
'uri' => '/res/9d21fb86/rsrc/js/application/conpherence/behavior-menu.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-workflow',
|
||||
3 => 'javelin-util',
|
||||
4 => 'javelin-stratcom',
|
||||
5 => 'javelin-uri',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/conpherence/behavior-menu.js',
|
||||
),
|
||||
'javelin-behavior-conpherence-widget-pane' =>
|
||||
array(
|
||||
'uri' => '/res/f3e0dbba/rsrc/js/application/conpherence/behavior-widget-pane.js',
|
||||
'type' => 'js',
|
||||
'requires' =>
|
||||
array(
|
||||
0 => 'javelin-behavior',
|
||||
1 => 'javelin-dom',
|
||||
2 => 'javelin-stratcom',
|
||||
),
|
||||
'disk' => '/rsrc/js/application/conpherence/behavior-widget-pane.js',
|
||||
),
|
||||
'javelin-behavior-countdown-timer' =>
|
||||
array(
|
||||
'uri' => '/res/7468acb7/rsrc/js/application/countdown/timer.js',
|
||||
|
@ -3254,7 +3338,7 @@ celerity_register_resource_map(array(
|
|||
), array(
|
||||
'packages' =>
|
||||
array(
|
||||
'd770a9ec' =>
|
||||
'8fd19266' =>
|
||||
array(
|
||||
'name' => 'core.pkg.css',
|
||||
'symbols' =>
|
||||
|
@ -3298,7 +3382,7 @@ celerity_register_resource_map(array(
|
|||
36 => 'phabricator-object-item-list-view-css',
|
||||
37 => 'global-drag-and-drop-css',
|
||||
),
|
||||
'uri' => '/res/pkg/d770a9ec/core.pkg.css',
|
||||
'uri' => '/res/pkg/8fd19266/core.pkg.css',
|
||||
'type' => 'css',
|
||||
),
|
||||
'66dca903' =>
|
||||
|
@ -3488,19 +3572,19 @@ celerity_register_resource_map(array(
|
|||
'reverse' =>
|
||||
array(
|
||||
'aphront-attached-file-view-css' => 'e30a3fa8',
|
||||
'aphront-crumbs-view-css' => 'd770a9ec',
|
||||
'aphront-dialog-view-css' => 'd770a9ec',
|
||||
'aphront-error-view-css' => 'd770a9ec',
|
||||
'aphront-form-view-css' => 'd770a9ec',
|
||||
'aphront-crumbs-view-css' => '8fd19266',
|
||||
'aphront-dialog-view-css' => '8fd19266',
|
||||
'aphront-error-view-css' => '8fd19266',
|
||||
'aphront-form-view-css' => '8fd19266',
|
||||
'aphront-headsup-action-list-view-css' => 'ec01d039',
|
||||
'aphront-headsup-view-css' => 'd770a9ec',
|
||||
'aphront-list-filter-view-css' => 'd770a9ec',
|
||||
'aphront-pager-view-css' => 'd770a9ec',
|
||||
'aphront-panel-view-css' => 'd770a9ec',
|
||||
'aphront-table-view-css' => 'd770a9ec',
|
||||
'aphront-tokenizer-control-css' => 'd770a9ec',
|
||||
'aphront-tooltip-css' => 'd770a9ec',
|
||||
'aphront-typeahead-control-css' => 'd770a9ec',
|
||||
'aphront-headsup-view-css' => '8fd19266',
|
||||
'aphront-list-filter-view-css' => '8fd19266',
|
||||
'aphront-pager-view-css' => '8fd19266',
|
||||
'aphront-panel-view-css' => '8fd19266',
|
||||
'aphront-table-view-css' => '8fd19266',
|
||||
'aphront-tokenizer-control-css' => '8fd19266',
|
||||
'aphront-tooltip-css' => '8fd19266',
|
||||
'aphront-typeahead-control-css' => '8fd19266',
|
||||
'differential-changeset-view-css' => 'ec01d039',
|
||||
'differential-core-view-css' => 'ec01d039',
|
||||
'differential-inline-comment-editor' => 'ac53d36a',
|
||||
|
@ -3514,7 +3598,7 @@ celerity_register_resource_map(array(
|
|||
'differential-table-of-contents-css' => 'ec01d039',
|
||||
'diffusion-commit-view-css' => 'c8ce2d88',
|
||||
'diffusion-icons-css' => 'c8ce2d88',
|
||||
'global-drag-and-drop-css' => 'd770a9ec',
|
||||
'global-drag-and-drop-css' => '8fd19266',
|
||||
'inline-comment-summary-css' => 'ec01d039',
|
||||
'javelin-aphlict' => '66dca903',
|
||||
'javelin-behavior' => 'fbeded59',
|
||||
|
@ -3584,48 +3668,48 @@ celerity_register_resource_map(array(
|
|||
'javelin-util' => 'fbeded59',
|
||||
'javelin-vector' => 'fbeded59',
|
||||
'javelin-workflow' => 'fbeded59',
|
||||
'lightbox-attachment-css' => 'd770a9ec',
|
||||
'lightbox-attachment-css' => '8fd19266',
|
||||
'maniphest-task-summary-css' => 'e30a3fa8',
|
||||
'maniphest-transaction-detail-css' => 'e30a3fa8',
|
||||
'phabricator-busy' => '66dca903',
|
||||
'phabricator-content-source-view-css' => 'ec01d039',
|
||||
'phabricator-core-buttons-css' => 'd770a9ec',
|
||||
'phabricator-core-css' => 'd770a9ec',
|
||||
'phabricator-crumbs-view-css' => 'd770a9ec',
|
||||
'phabricator-directory-css' => 'd770a9ec',
|
||||
'phabricator-core-buttons-css' => '8fd19266',
|
||||
'phabricator-core-css' => '8fd19266',
|
||||
'phabricator-crumbs-view-css' => '8fd19266',
|
||||
'phabricator-directory-css' => '8fd19266',
|
||||
'phabricator-drag-and-drop-file-upload' => 'ac53d36a',
|
||||
'phabricator-dropdown-menu' => '66dca903',
|
||||
'phabricator-file-upload' => '66dca903',
|
||||
'phabricator-filetree-view-css' => 'd770a9ec',
|
||||
'phabricator-flag-css' => 'd770a9ec',
|
||||
'phabricator-form-view-css' => 'd770a9ec',
|
||||
'phabricator-header-view-css' => 'd770a9ec',
|
||||
'phabricator-jump-nav' => 'd770a9ec',
|
||||
'phabricator-filetree-view-css' => '8fd19266',
|
||||
'phabricator-flag-css' => '8fd19266',
|
||||
'phabricator-form-view-css' => '8fd19266',
|
||||
'phabricator-header-view-css' => '8fd19266',
|
||||
'phabricator-jump-nav' => '8fd19266',
|
||||
'phabricator-keyboard-shortcut' => '66dca903',
|
||||
'phabricator-keyboard-shortcut-manager' => '66dca903',
|
||||
'phabricator-main-menu-view' => 'd770a9ec',
|
||||
'phabricator-main-menu-view' => '8fd19266',
|
||||
'phabricator-menu-item' => '66dca903',
|
||||
'phabricator-nav-view-css' => 'd770a9ec',
|
||||
'phabricator-nav-view-css' => '8fd19266',
|
||||
'phabricator-notification' => '66dca903',
|
||||
'phabricator-notification-css' => 'd770a9ec',
|
||||
'phabricator-notification-menu-css' => 'd770a9ec',
|
||||
'phabricator-object-item-list-view-css' => 'd770a9ec',
|
||||
'phabricator-notification-css' => '8fd19266',
|
||||
'phabricator-notification-menu-css' => '8fd19266',
|
||||
'phabricator-object-item-list-view-css' => '8fd19266',
|
||||
'phabricator-object-selector-css' => 'ec01d039',
|
||||
'phabricator-paste-file-upload' => '66dca903',
|
||||
'phabricator-prefab' => '66dca903',
|
||||
'phabricator-project-tag-css' => 'e30a3fa8',
|
||||
'phabricator-remarkup-css' => 'd770a9ec',
|
||||
'phabricator-remarkup-css' => '8fd19266',
|
||||
'phabricator-shaped-request' => 'ac53d36a',
|
||||
'phabricator-side-menu-view-css' => 'd770a9ec',
|
||||
'phabricator-standard-page-view' => 'd770a9ec',
|
||||
'phabricator-side-menu-view-css' => '8fd19266',
|
||||
'phabricator-standard-page-view' => '8fd19266',
|
||||
'phabricator-textareautils' => '66dca903',
|
||||
'phabricator-tooltip' => '66dca903',
|
||||
'phabricator-transaction-view-css' => 'd770a9ec',
|
||||
'phabricator-zindex-css' => 'd770a9ec',
|
||||
'sprite-apps-large-css' => 'd770a9ec',
|
||||
'sprite-gradient-css' => 'd770a9ec',
|
||||
'sprite-icon-css' => 'd770a9ec',
|
||||
'sprite-menu-css' => 'd770a9ec',
|
||||
'syntax-highlighting-css' => 'd770a9ec',
|
||||
'phabricator-transaction-view-css' => '8fd19266',
|
||||
'phabricator-zindex-css' => '8fd19266',
|
||||
'sprite-apps-large-css' => '8fd19266',
|
||||
'sprite-gradient-css' => '8fd19266',
|
||||
'sprite-icon-css' => '8fd19266',
|
||||
'sprite-menu-css' => '8fd19266',
|
||||
'syntax-highlighting-css' => '8fd19266',
|
||||
),
|
||||
));
|
||||
|
|
|
@ -195,6 +195,25 @@ phutil_register_library_map(array(
|
|||
'ConduitCallTestCase' => 'applications/conduit/call/__tests__/ConduitCallTestCase.php',
|
||||
'ConduitException' => 'applications/conduit/protocol/ConduitException.php',
|
||||
'ConduitSSHWorkflow' => 'applications/conduit/ssh/ConduitSSHWorkflow.php',
|
||||
'ConpherenceConstants' => 'applications/conpherence/constants/ConpherenceConstants.php',
|
||||
'ConpherenceController' => 'applications/conpherence/controller/ConpherenceController.php',
|
||||
'ConpherenceDAO' => 'applications/conpherence/storage/ConpherenceDAO.php',
|
||||
'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php',
|
||||
'ConpherenceListController' => 'applications/conpherence/controller/ConpherenceListController.php',
|
||||
'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php',
|
||||
'ConpherenceNewController' => 'applications/conpherence/controller/ConpherenceNewController.php',
|
||||
'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php',
|
||||
'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php',
|
||||
'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php',
|
||||
'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php',
|
||||
'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php',
|
||||
'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php',
|
||||
'ConpherenceTransactionComment' => 'applications/conpherence/storage/ConpherenceTransactionComment.php',
|
||||
'ConpherenceTransactionQuery' => 'applications/conpherence/query/ConpherenceTransactionQuery.php',
|
||||
'ConpherenceTransactionType' => 'applications/conpherence/constants/ConpherenceTransactionType.php',
|
||||
'ConpherenceTransactionView' => 'applications/conpherence/view/ConpherenceTransactionView.php',
|
||||
'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php',
|
||||
'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php',
|
||||
'DarkConsoleController' => 'aphront/console/DarkConsoleController.php',
|
||||
'DarkConsoleCore' => 'aphront/console/DarkConsoleCore.php',
|
||||
'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/DarkConsoleErrorLogPlugin.php',
|
||||
|
@ -602,6 +621,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationConduit' => 'applications/conduit/application/PhabricatorApplicationConduit.php',
|
||||
'PhabricatorApplicationConfig' => 'applications/config/application/PhabricatorApplicationConfig.php',
|
||||
'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php',
|
||||
'PhabricatorApplicationConpherence' => 'applications/conpherence/application/PhabricatorApplicationConpherence.php',
|
||||
'PhabricatorApplicationCountdown' => 'applications/countdown/application/PhabricatorApplicationCountdown.php',
|
||||
'PhabricatorApplicationDaemons' => 'applications/daemon/application/PhabricatorApplicationDaemons.php',
|
||||
'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php',
|
||||
|
@ -1646,6 +1666,28 @@ phutil_register_library_map(array(
|
|||
'ConduitCallTestCase' => 'PhabricatorTestCase',
|
||||
'ConduitException' => 'Exception',
|
||||
'ConduitSSHWorkflow' => 'PhabricatorSSHWorkflow',
|
||||
'ConpherenceController' => 'PhabricatorController',
|
||||
'ConpherenceDAO' => 'PhabricatorLiskDAO',
|
||||
'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor',
|
||||
'ConpherenceListController' => 'ConpherenceController',
|
||||
'ConpherenceMenuItemView' => 'AphrontTagView',
|
||||
'ConpherenceNewController' => 'ConpherenceController',
|
||||
'ConpherenceParticipant' => 'ConpherenceDAO',
|
||||
'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery',
|
||||
'ConpherenceParticipationStatus' => 'ConpherenceConstants',
|
||||
'ConpherenceThread' =>
|
||||
array(
|
||||
0 => 'ConpherenceDAO',
|
||||
1 => 'PhabricatorPolicyInterface',
|
||||
),
|
||||
'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
|
||||
'ConpherenceTransaction' => 'PhabricatorApplicationTransaction',
|
||||
'ConpherenceTransactionComment' => 'PhabricatorApplicationTransactionComment',
|
||||
'ConpherenceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'ConpherenceTransactionType' => 'ConpherenceConstants',
|
||||
'ConpherenceTransactionView' => 'AphrontView',
|
||||
'ConpherenceUpdateController' => 'ConpherenceController',
|
||||
'ConpherenceViewController' => 'ConpherenceController',
|
||||
'DarkConsoleController' => 'PhabricatorController',
|
||||
'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin',
|
||||
'DarkConsoleEventPlugin' => 'DarkConsolePlugin',
|
||||
|
@ -1998,6 +2040,7 @@ phutil_register_library_map(array(
|
|||
'PhabricatorApplicationConduit' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationConfig' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationConfigOptions' => 'Phobject',
|
||||
'PhabricatorApplicationConpherence' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationCountdown' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationDaemons' => 'PhabricatorApplication',
|
||||
'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class PhabricatorApplicationConpherence extends PhabricatorApplication {
|
||||
|
||||
public function isBeta() {
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getBaseURI() {
|
||||
return '/conpherence/';
|
||||
}
|
||||
|
||||
public function getQuickCreateURI() {
|
||||
return $this->getBaseURI().'new/';
|
||||
}
|
||||
|
||||
public function getShortDescription() {
|
||||
return pht('Messaging');
|
||||
}
|
||||
|
||||
public function getIconName() {
|
||||
return 'conpherence';
|
||||
}
|
||||
|
||||
public function getTitleGlyph() {
|
||||
return "\xE2\x98\x8E";
|
||||
}
|
||||
|
||||
public function getApplicationGroup() {
|
||||
return self::GROUP_COMMUNICATION;
|
||||
}
|
||||
|
||||
public function getRoutes() {
|
||||
return array(
|
||||
'/conpherence/' => array(
|
||||
'' => 'ConpherenceListController',
|
||||
'new/' => 'ConpherenceNewController',
|
||||
'view/(?P<id>[1-9]\d*)/' => 'ConpherenceViewController',
|
||||
'update/(?P<id>[1-9]\d*)/' => 'ConpherenceUpdateController',
|
||||
'(?P<id>[1-9]\d*)/' => 'ConpherenceListController',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
abstract class ConpherenceConstants {
|
||||
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
final class ConpherenceParticipationStatus extends ConpherenceConstants {
|
||||
|
||||
const UP_TO_DATE = 0;
|
||||
const BEHIND = 1;
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceTransactionType extends ConpherenceConstants {
|
||||
|
||||
const TYPE_FILES = 'files';
|
||||
const TYPE_TITLE = 'title';
|
||||
const TYPE_PICTURE = 'picture';
|
||||
const TYPE_PARTICIPANTS = 'participants';
|
||||
|
||||
}
|
|
@ -0,0 +1,225 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
abstract class ConpherenceController extends PhabricatorController {
|
||||
private $conpherences;
|
||||
private $selectedConpherencePHID;
|
||||
private $readConpherences;
|
||||
private $unreadConpherences;
|
||||
|
||||
public function setUnreadConpherences(array $conpherences) {
|
||||
assert_instances_of($conpherences, 'ConpherenceThread');
|
||||
$this->unreadConpherences = $conpherences;
|
||||
return $this;
|
||||
}
|
||||
public function getUnreadConpherences() {
|
||||
return $this->unreadConpherences;
|
||||
}
|
||||
|
||||
public function setReadConpherences(array $conpherences) {
|
||||
assert_instances_of($conpherences, 'ConpherenceThread');
|
||||
$this->readConpherences = $conpherences;
|
||||
return $this;
|
||||
}
|
||||
public function getReadConpherences() {
|
||||
return $this->readConpherences;
|
||||
}
|
||||
|
||||
public function setSelectedConpherencePHID($phid) {
|
||||
$this->selectedConpherencePHID = $phid;
|
||||
return $this;
|
||||
}
|
||||
public function getSelectedConpherencePHID() {
|
||||
return $this->selectedConpherencePHID;
|
||||
}
|
||||
|
||||
/**
|
||||
* Try for a full set of unread conpherences, and if we fail
|
||||
* load read conpherences. Additional conpherences in either category
|
||||
* are loaded asynchronously.
|
||||
*/
|
||||
public function loadStartingConpherences($current_selection_epoch = null) {
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$read_participant_query = id(new ConpherenceParticipantQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()));
|
||||
$read_status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
if ($current_selection_epoch) {
|
||||
$read_one = $read_participant_query
|
||||
->withParticipationStatus($read_status)
|
||||
->withDateTouched($current_selection_epoch, '>')
|
||||
->execute();
|
||||
|
||||
$read_two = $read_participant_query
|
||||
->withDateTouched($current_selection_epoch, '<=')
|
||||
->execute();
|
||||
|
||||
$read = array_merge($read_one, $read_two);
|
||||
|
||||
} else {
|
||||
$read = $read_participant_query
|
||||
->withParticipationStatus($read_status)
|
||||
->execute();
|
||||
}
|
||||
|
||||
$unread_status = ConpherenceParticipationStatus::BEHIND;
|
||||
$unread_participant_query = id(new ConpherenceParticipantQuery())
|
||||
->withParticipantPHIDs(array($user->getPHID()));
|
||||
$unread = $unread_participant_query
|
||||
->withParticipationStatus($unread_status)
|
||||
->execute();
|
||||
|
||||
$all_participation = $unread + $read;
|
||||
$all_conpherence_phids = array_keys($all_participation);
|
||||
$all_conpherences = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->withPHIDs($all_conpherence_phids)
|
||||
->execute();
|
||||
$unread_conpherences = array_select_keys(
|
||||
$all_conpherences,
|
||||
array_keys($unread)
|
||||
);
|
||||
$this->setUnreadConpherences($unread_conpherences);
|
||||
|
||||
$read_conpherences = array_select_keys(
|
||||
$all_conpherences,
|
||||
array_keys($read)
|
||||
);
|
||||
$this->setReadConpherences($read_conpherences);
|
||||
|
||||
if (!$this->getSelectedConpherencePHID()) {
|
||||
$this->setSelectedConpherencePHID(reset($all_conpherence_phids));
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function buildSideNavView($filter = null) {
|
||||
require_celerity_resource('conpherence-menu-css');
|
||||
$unread_conpherences = $this->getUnreadConpherences();
|
||||
$read_conpherences = $this->getReadConpherences();
|
||||
|
||||
$user = $this->getRequest()->getUser();
|
||||
|
||||
$menu = new PhabricatorMenuView();
|
||||
$nav = AphrontSideNavFilterView::newFromMenu($menu);
|
||||
$nav->addClass('conpherence-menu');
|
||||
$nav->setMenuID('conpherence-menu');
|
||||
|
||||
$nav->addFilter(
|
||||
'new',
|
||||
pht('New Conpherence'),
|
||||
$this->getApplicationURI('new/')
|
||||
);
|
||||
$nav->addLabel(pht('Unread'));
|
||||
$nav = $this->addConpherencesToNav($unread_conpherences, $nav);
|
||||
|
||||
$nav->addLabel(pht('Read'));
|
||||
$nav = $this->addConpherencesToNav($read_conpherences, $nav, true);
|
||||
|
||||
$nav->selectFilter($filter);
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
private function addConpherencesToNav(
|
||||
array $conpherences,
|
||||
AphrontSideNavFilterView $nav,
|
||||
$read = false) {
|
||||
|
||||
$user = $this->getRequest()->getUser();
|
||||
foreach ($conpherences as $conpherence) {
|
||||
$uri = $this->getApplicationURI('view/'.$conpherence->getID().'/');
|
||||
$data = $conpherence->getDisplayData($user);
|
||||
$title = $data['title'];
|
||||
$subtitle = $data['subtitle'];
|
||||
$unread_count = $data['unread_count'];
|
||||
$epoch = $data['epoch'];
|
||||
$image = $data['image'];
|
||||
$snippet = $data['snippet'];
|
||||
|
||||
$item = id(new ConpherenceMenuItemView())
|
||||
->setUser($user)
|
||||
->setTitle($title)
|
||||
->setSubtitle($subtitle)
|
||||
->setHref($uri)
|
||||
->setEpoch($epoch)
|
||||
->setImageURI($image)
|
||||
->setMessageText($snippet)
|
||||
->setUnreadCount($unread_count)
|
||||
->setID($conpherence->getPHID())
|
||||
->addSigil('conpherence-menu-click')
|
||||
->setMetadata(array('id' => $conpherence->getID()));
|
||||
if ($this->getSelectedConpherencePHID() == $conpherence->getPHID()) {
|
||||
$item->addClass('conpherence-selected');
|
||||
$item->addClass('hide-unread-count');
|
||||
}
|
||||
$nav->addCustomBlock($item->render());
|
||||
}
|
||||
if (empty($conpherences) || $read) {
|
||||
$nav->addCustomBlock($this->getNoConpherencesBlock());
|
||||
}
|
||||
|
||||
return $nav;
|
||||
}
|
||||
|
||||
private function getNoConpherencesBlock() {
|
||||
|
||||
return phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'no-conpherences-menu-item'
|
||||
),
|
||||
pht('No more conpherences.')
|
||||
);
|
||||
}
|
||||
|
||||
public function buildApplicationMenu() {
|
||||
return $this->buildSideNavView()->getMenu();
|
||||
}
|
||||
|
||||
public function buildApplicationCrumbs() {
|
||||
$crumbs = parent::buildApplicationCrumbs();
|
||||
|
||||
$crumbs
|
||||
->addAction(
|
||||
id(new PhabricatorMenuItemView())
|
||||
->setName(pht('New Conpherence'))
|
||||
->setHref($this->getApplicationURI('new/'))
|
||||
->setIcon('create')
|
||||
)
|
||||
->addCrumb(
|
||||
id(new PhabricatorCrumbView())
|
||||
->setName(pht('Conpherence'))
|
||||
);
|
||||
|
||||
return $crumbs;
|
||||
}
|
||||
|
||||
protected function initJavelinBehaviors() {
|
||||
|
||||
Javelin::initBehavior('conpherence-menu',
|
||||
array(
|
||||
'base_uri' => $this->getApplicationURI(''),
|
||||
'header' => 'conpherence-header-pane',
|
||||
'messages' => 'conpherence-messages',
|
||||
'widgets_pane' => 'conpherence-widget-pane',
|
||||
'form_pane' => 'conpherence-form',
|
||||
'fancy_ajax' => (bool) $this->getSelectedConpherencePHID()
|
||||
)
|
||||
);
|
||||
Javelin::initBehavior('conpherence-init',
|
||||
array(
|
||||
'selected_conpherence_id' => $this->getSelectedConpherencePHID(),
|
||||
'menu_pane' => 'conpherence-menu',
|
||||
'messages_pane' => 'conpherence-message-pane',
|
||||
'messages' => 'conpherence-messages',
|
||||
'widgets_pane' => 'conpherence-widget-pane',
|
||||
'form_pane' => 'conpherence-form'
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,119 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceListController extends
|
||||
ConpherenceController {
|
||||
|
||||
private $conpherenceID;
|
||||
|
||||
public function setConpherenceID($conpherence_id) {
|
||||
$this->conpherenceID = $conpherence_id;
|
||||
return $this;
|
||||
}
|
||||
public function getConpherenceID() {
|
||||
return $this->conpherenceID;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->setConpherenceID(idx($data, 'id'));
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$title = pht('Conpherence');
|
||||
|
||||
$conpherence_id = $this->getConpherenceID();
|
||||
$current_selection_epoch = null;
|
||||
if ($conpherence_id) {
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($conpherence_id))
|
||||
->executeOne();
|
||||
if (!$conpherence) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
if ($conpherence->getTitle()) {
|
||||
$title = $conpherence->getTitle();
|
||||
}
|
||||
$this->setSelectedConpherencePHID($conpherence->getPHID());
|
||||
|
||||
$read_status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$participant = $conpherence->getParticipant($user->getPHID());
|
||||
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$participant->markUpToDate();
|
||||
unset($write_guard);
|
||||
$current_selection_epoch = $participant->getDateTouched();
|
||||
}
|
||||
|
||||
$this->loadStartingConpherences($current_selection_epoch);
|
||||
$nav = $this->buildSideNavView();
|
||||
|
||||
$main_pane = $this->renderEmptyMainPane();
|
||||
$nav->appendChild(
|
||||
array(
|
||||
$main_pane,
|
||||
));
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$nav,
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
private function renderEmptyMainPane() {
|
||||
$this->initJavelinBehaviors();
|
||||
return phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => 'conpherence-main-pane'
|
||||
),
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'conpherence-header-pane',
|
||||
'id' => 'conpherence-header-pane',
|
||||
),
|
||||
''
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'conpherence-widget-pane',
|
||||
'id' => 'conpherence-widget-pane'
|
||||
),
|
||||
''
|
||||
).
|
||||
javelin_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'conpherence-message-pane',
|
||||
'id' => 'conpherence-message-pane'
|
||||
),
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'conpherence-messages',
|
||||
'id' => 'conpherence-messages'
|
||||
),
|
||||
''
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'id' => 'conpherence-form'
|
||||
),
|
||||
''
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -0,0 +1,201 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceNewController extends ConpherenceController {
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$conpherence = id(new ConpherenceThread())
|
||||
->attachParticipants(array())
|
||||
->attachFilePHIDs(array());
|
||||
$title = pht('New Conpherence');
|
||||
$participants = array();
|
||||
$message = '';
|
||||
$files = array();
|
||||
$errors = array();
|
||||
$e_participants = null;
|
||||
$e_message = null;
|
||||
|
||||
// this comes from ajax requests from all over. should be a single phid.
|
||||
$participant_prefill = $request->getStr('participant');
|
||||
if ($participant_prefill) {
|
||||
$participants[] = $participant_prefill;
|
||||
}
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
$participants = $request->getArr('participants');
|
||||
if (empty($participants)) {
|
||||
$e_participants = true;
|
||||
$errors[] = pht('You must specify participants.');
|
||||
} else {
|
||||
$participants[] = $user->getPHID();
|
||||
$participants = array_unique($participants);
|
||||
}
|
||||
|
||||
$message = $request->getStr('message');
|
||||
if (empty($message)) {
|
||||
$e_message = true;
|
||||
$errors[] = pht('You must write a message.');
|
||||
}
|
||||
|
||||
$file_phids =
|
||||
PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
|
||||
array($message)
|
||||
);
|
||||
if ($file_phids) {
|
||||
$files = id(new PhabricatorFileQuery())
|
||||
->setViewer($user)
|
||||
->withPHIDs($file_phids)
|
||||
->execute();
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$conpherence->openTransaction();
|
||||
$conpherence->save();
|
||||
$xactions = array();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransactionType::TYPE_PARTICIPANTS)
|
||||
->setNewValue(array('+' => $participants));
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
->attachComment(
|
||||
id(new ConpherenceTransactionComment())
|
||||
->setContent($message)
|
||||
->setConpherencePHID($conpherence->getPHID())
|
||||
);
|
||||
if ($files) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransactionType::TYPE_FILES)
|
||||
->setNewValue(array('+' => mpull($files, 'getPHID')));
|
||||
}
|
||||
$content_source = PhabricatorContentSource::newForSource(
|
||||
PhabricatorContentSource::SOURCE_WEB,
|
||||
array(
|
||||
'ip' => $request->getRemoteAddr()
|
||||
)
|
||||
);
|
||||
id(new ConpherenceEditor())
|
||||
->setContentSource($content_source)
|
||||
->setContinueOnNoEffect(true)
|
||||
->setActor($user)
|
||||
->applyTransactions($conpherence, $xactions);
|
||||
|
||||
$conpherence->saveTransaction();
|
||||
|
||||
if ($request->isAjax()) {
|
||||
$dialog = id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->setTitle('Success')
|
||||
->addCancelButton('#', 'Okay')
|
||||
->appendChild(
|
||||
phutil_render_tag('p',
|
||||
array(),
|
||||
pht('Message sent successfully.')
|
||||
)
|
||||
);
|
||||
$response = id(new AphrontDialogResponse())
|
||||
->setDialog($dialog);
|
||||
} else {
|
||||
$uri = $this->getApplicationURI($conpherence->getID());
|
||||
$response = id(new AphrontRedirectResponse())
|
||||
->setURI($uri);
|
||||
}
|
||||
return $response;
|
||||
}
|
||||
}
|
||||
|
||||
$error_view = null;
|
||||
if ($errors) {
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setTitle(pht('Conpherence Errors'))
|
||||
->setErrors($errors);
|
||||
}
|
||||
|
||||
$participant_handles = array();
|
||||
if ($participants) {
|
||||
$handles = id(new PhabricatorObjectHandleData($participants))
|
||||
->setViewer($user)
|
||||
->loadHandles();
|
||||
$participant_handles = mpull($handles, 'getFullName', 'getPHID');
|
||||
}
|
||||
|
||||
$submit_uri = $this->getApplicationURI('new/');
|
||||
$cancel_uri = $this->getApplicationURI();
|
||||
if ($request->isAjax()) {
|
||||
// TODO - we can get a better cancel_uri once we get better at crazy
|
||||
// ajax jonx T2086
|
||||
if ($participant_prefill) {
|
||||
$handle = $handles[$participant_prefill];
|
||||
$cancel_uri = $handle->getURI();
|
||||
}
|
||||
$form = id(new AphrontDialogView())
|
||||
->setSubmitURI($submit_uri)
|
||||
->addSubmitButton()
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->addCancelButton($cancel_uri);
|
||||
} else {
|
||||
$form = id(new AphrontFormView())
|
||||
->setID('conpherence-message-pane')
|
||||
->setAction($submit_uri);
|
||||
}
|
||||
|
||||
$form
|
||||
->setUser($user)
|
||||
->appendChild(
|
||||
id(new AphrontFormTokenizerControl())
|
||||
->setName('participants')
|
||||
->setValue($participant_handles)
|
||||
->setUser($user)
|
||||
->setDatasource('/typeahead/common/users/')
|
||||
->setLabel(pht('To'))
|
||||
->setError($e_participants)
|
||||
)
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setName('message')
|
||||
->setValue($message)
|
||||
->setLabel(pht('Message'))
|
||||
->setPlaceHolder(pht('Drag and drop to include files...'))
|
||||
->setError($e_message)
|
||||
);
|
||||
|
||||
if ($request->isAjax()) {
|
||||
$form->setTitle($title);
|
||||
return id(new AphrontDialogResponse())
|
||||
->setDialog($form);
|
||||
}
|
||||
|
||||
$form
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Submit'))
|
||||
->addCancelButton($cancel_uri)
|
||||
);
|
||||
|
||||
$this->loadStartingConpherences();
|
||||
$this->setSelectedConpherencePHID(null);
|
||||
$this->initJavelinBehaviors();
|
||||
$nav = $this->buildSideNavView('new');
|
||||
$header = id(new PhabricatorHeaderView())
|
||||
->setHeader($title);
|
||||
|
||||
$nav->appendChild(
|
||||
array(
|
||||
$header,
|
||||
$error_view,
|
||||
$form,
|
||||
));
|
||||
|
||||
return $this->buildApplicationPage(
|
||||
$nav,
|
||||
array(
|
||||
'title' => $title,
|
||||
'device' => true,
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,219 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceUpdateController extends
|
||||
ConpherenceController {
|
||||
|
||||
private $conpherenceID;
|
||||
|
||||
public function setConpherenceID($conpherence_id) {
|
||||
$this->conpherenceID = $conpherence_id;
|
||||
return $this;
|
||||
}
|
||||
public function getConpherenceID() {
|
||||
return $this->conpherenceID;
|
||||
}
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->setConpherenceID(idx($data, 'id'));
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$conpherence_id = $this->getConpherenceID();
|
||||
if (!$conpherence_id) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($conpherence_id))
|
||||
->executeOne();
|
||||
$supported_formats = PhabricatorFile::getTransformableImageFormats();
|
||||
|
||||
$updated = false;
|
||||
$error_view = null;
|
||||
$e_image = null;
|
||||
$errors = array();
|
||||
if ($request->isFormPost()) {
|
||||
$content_source = PhabricatorContentSource::newForSource(
|
||||
PhabricatorContentSource::SOURCE_WEB,
|
||||
array(
|
||||
'ip' => $request->getRemoteAddr()
|
||||
));
|
||||
|
||||
$action = $request->getStr('action');
|
||||
switch ($action) {
|
||||
case 'message':
|
||||
$message = $request->getStr('text');
|
||||
$files = array();
|
||||
$file_phids =
|
||||
PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
|
||||
array($message)
|
||||
);
|
||||
if ($file_phids) {
|
||||
$files = id(new PhabricatorFileQuery())
|
||||
->setViewer($user)
|
||||
->withPHIDs($file_phids)
|
||||
->execute();
|
||||
}
|
||||
$xactions = array();
|
||||
if ($files) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransactionType::TYPE_FILES)
|
||||
->setNewValue(array('+' => mpull($files, 'getPHID')));
|
||||
}
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(PhabricatorTransactions::TYPE_COMMENT)
|
||||
->attachComment(
|
||||
id(new ConpherenceTransactionComment())
|
||||
->setContent($message)
|
||||
->setConpherencePHID($conpherence->getPHID())
|
||||
);
|
||||
$time = time();
|
||||
$conpherence->openTransaction();
|
||||
$xactions = id(new ConpherenceEditor())
|
||||
->setContentSource($content_source)
|
||||
->setActor($user)
|
||||
->applyTransactions($conpherence, $xactions);
|
||||
$last_xaction = end($xactions);
|
||||
$xaction_phid = $last_xaction->getPHID();
|
||||
$behind = ConpherenceParticipationStatus::BEHIND;
|
||||
$up_to_date = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
$participants = $conpherence->getParticipants();
|
||||
foreach ($participants as $phid => $participant) {
|
||||
if ($phid != $user->getPHID()) {
|
||||
if ($participant->getParticipationStatus() != $behind) {
|
||||
$participant->setBehindTransactionPHID($xaction_phid);
|
||||
}
|
||||
$participant->setParticipationStatus($behind);
|
||||
$participant->setDateTouched($time);
|
||||
} else {
|
||||
$participant->setParticipationStatus($up_to_date);
|
||||
$participant->setDateTouched($time);
|
||||
}
|
||||
$participant->save();
|
||||
}
|
||||
$updated = $conpherence->saveTransaction();
|
||||
break;
|
||||
case 'metadata':
|
||||
$xactions = array();
|
||||
$default_image = $request->getExists('default_image');
|
||||
if ($default_image) {
|
||||
$image_phid = null;
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransactionType::TYPE_PICTURE)
|
||||
->setNewValue($image_phid);
|
||||
} else if (!empty($_FILES['image'])) {
|
||||
$err = idx($_FILES['image'], 'error');
|
||||
if ($err != UPLOAD_ERR_NO_FILE) {
|
||||
$file = PhabricatorFile::newFromPHPUpload(
|
||||
$_FILES['image'],
|
||||
array(
|
||||
'authorPHID' => $user->getPHID(),
|
||||
));
|
||||
$okay = $file->isTransformableImage();
|
||||
if ($okay) {
|
||||
$xformer = new PhabricatorImageTransformer();
|
||||
$xformed = $xformer->executeThumbTransform(
|
||||
$file,
|
||||
$x = 50,
|
||||
$y = 50);
|
||||
$image_phid = $xformed->getPHID();
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransactionType::TYPE_PICTURE)
|
||||
->setNewValue($image_phid);
|
||||
} else {
|
||||
$e_image = pht('Not Supported');
|
||||
$errors[] =
|
||||
pht('This server only supports these image formats: %s.',
|
||||
implode(', ', $supported_formats));
|
||||
}
|
||||
}
|
||||
}
|
||||
$title = $request->getStr('title');
|
||||
if ($title != $conpherence->getTitle()) {
|
||||
$xactions[] = id(new ConpherenceTransaction())
|
||||
->setTransactionType(ConpherenceTransactionType::TYPE_TITLE)
|
||||
->setNewValue($title);
|
||||
}
|
||||
|
||||
if ($xactions) {
|
||||
$conpherence->openTransaction();
|
||||
$xactions = id(new ConpherenceEditor())
|
||||
->setContentSource($content_source)
|
||||
->setActor($user)
|
||||
->setContinueOnNoEffect(true)
|
||||
->applyTransactions($conpherence, $xactions);
|
||||
$updated = $conpherence->saveTransaction();
|
||||
} else if (empty($errors)) {
|
||||
$errors[] = pht(
|
||||
'That was a non-update. Try cancel.'
|
||||
);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown action: '.$action);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($updated) {
|
||||
return id(new AphrontRedirectResponse())->setURI(
|
||||
$this->getApplicationURI($conpherence_id.'/')
|
||||
);
|
||||
}
|
||||
|
||||
if ($errors) {
|
||||
$error_view = id(new AphrontErrorView())
|
||||
->setTitle(pht('Errors editing conpherence.'))
|
||||
->setErrors($errors);
|
||||
}
|
||||
|
||||
$form = id(new AphrontFormLayoutView())
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel(pht('Title'))
|
||||
->setName('title')
|
||||
->setValue($conpherence->getTitle())
|
||||
)
|
||||
->appendChild(
|
||||
id(new AphrontFormMarkupControl())
|
||||
->setLabel(pht('Image'))
|
||||
->setValue(phutil_render_tag(
|
||||
'img',
|
||||
array(
|
||||
'src' => $conpherence->loadImageURI(),
|
||||
))
|
||||
)
|
||||
)
|
||||
->appendChild(
|
||||
id(new AphrontFormImageControl())
|
||||
->setLabel(pht('Change Image'))
|
||||
->setName('image')
|
||||
->setCaption('Supported formats: '.implode(', ', $supported_formats))
|
||||
->setError($e_image)
|
||||
);
|
||||
|
||||
// TODO -- fix javelin so we can upload files from a workflow
|
||||
require_celerity_resource('conpherence-update-css');
|
||||
return $this->buildStandardPageResponse(
|
||||
array(
|
||||
$error_view,
|
||||
id(new AphrontDialogView())
|
||||
->setUser($user)
|
||||
->setTitle(pht('Update Conpherence'))
|
||||
->setWidth(AphrontDialogView::WIDTH_FORM)
|
||||
->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/'))
|
||||
->setEncType('multipart/form-data')
|
||||
->addHiddenInput('action', 'metadata')
|
||||
->appendChild($form)
|
||||
->addSubmitButton()
|
||||
->addCancelButton($this->getApplicationURI($conpherence->getID().'/')),
|
||||
),
|
||||
array()
|
||||
);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,291 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceViewController extends
|
||||
ConpherenceController {
|
||||
|
||||
private $conpherenceID;
|
||||
private $conpherence;
|
||||
|
||||
public function setConpherence(ConpherenceThread $conpherence) {
|
||||
$this->conpherence = $conpherence;
|
||||
return $this;
|
||||
}
|
||||
public function getConpherence() {
|
||||
return $this->conpherence;
|
||||
}
|
||||
|
||||
public function setConpherenceID($conpherence_id) {
|
||||
$this->conpherenceID = $conpherence_id;
|
||||
return $this;
|
||||
}
|
||||
public function getConpherenceID() {
|
||||
return $this->conpherenceID;
|
||||
}
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->setConpherenceID(idx($data, 'id'));
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
|
||||
$conpherence_id = $this->getConpherenceID();
|
||||
if (!$conpherence_id) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
if (!$request->isAjax()) {
|
||||
return id(new AphrontRedirectResponse())
|
||||
->setURI($this->getApplicationURI($conpherence_id.'/'));
|
||||
}
|
||||
$conpherence = id(new ConpherenceThreadQuery())
|
||||
->setViewer($user)
|
||||
->withIDs(array($conpherence_id))
|
||||
->needWidgetData(true)
|
||||
->executeOne();
|
||||
$this->setConpherence($conpherence);
|
||||
|
||||
$participant = $conpherence->getParticipant($user->getPHID());
|
||||
$write_guard = AphrontWriteGuard::beginScopedUnguardedWrites();
|
||||
$participant->markUpToDate();
|
||||
unset($write_guard);
|
||||
|
||||
$header = $this->renderHeaderPaneContent();
|
||||
$messages = $this->renderMessagePaneContent();
|
||||
$widgets = $this->renderWidgetPaneContent();
|
||||
$content = $header + $widgets + $messages;
|
||||
|
||||
return id(new AphrontAjaxResponse())->setContent($content);
|
||||
}
|
||||
|
||||
private function renderHeaderPaneContent() {
|
||||
require_celerity_resource('conpherence-header-pane-css');
|
||||
$user = $this->getRequest()->getUser();
|
||||
$conpherence = $this->getConpherence();
|
||||
$display_data = $conpherence->getDisplayData($user);
|
||||
$edit_href = $this->getApplicationURI('update/'.$conpherence->getID().'/');
|
||||
|
||||
$header =
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'class' => 'edit',
|
||||
'href' => $edit_href,
|
||||
),
|
||||
pht('edit...')
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'header-image',
|
||||
'style' => 'background-image: url('.$display_data['image'].');'
|
||||
),
|
||||
''
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'title',
|
||||
),
|
||||
phutil_escape_html($display_data['title'])
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'subtitle',
|
||||
),
|
||||
phutil_escape_html($display_data['subtitle'])
|
||||
);
|
||||
|
||||
return array('header' => $header);
|
||||
}
|
||||
|
||||
private function renderMessagePaneContent() {
|
||||
require_celerity_resource('conpherence-message-pane-css');
|
||||
$user = $this->getRequest()->getUser();
|
||||
$conpherence = $this->getConpherence();
|
||||
$handles = $conpherence->getHandles();
|
||||
$rendered_transactions = array();
|
||||
|
||||
$transactions = $conpherence->getTransactions();
|
||||
foreach ($transactions as $transaction) {
|
||||
$rendered_transactions[] = id(new ConpherenceTransactionView())
|
||||
->setUser($user)
|
||||
->setConpherenceTransaction($transaction)
|
||||
->setHandles($handles)
|
||||
->render();
|
||||
}
|
||||
$transactions = implode(' ', $rendered_transactions);
|
||||
|
||||
$form =
|
||||
id(new AphrontFormView())
|
||||
->setAction($this->getApplicationURI('update/'.$conpherence->getID().'/'))
|
||||
->setFlexible(true)
|
||||
->setUser($user)
|
||||
->addHiddenInput('action', 'message')
|
||||
->appendChild(
|
||||
id(new PhabricatorRemarkupControl())
|
||||
->setUser($user)
|
||||
->setName('text')
|
||||
)
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->setValue(pht('Pontificate'))
|
||||
)->render();
|
||||
|
||||
return array(
|
||||
'messages' => $transactions,
|
||||
'form' => $form
|
||||
);
|
||||
|
||||
}
|
||||
|
||||
private function renderWidgetPaneContent() {
|
||||
require_celerity_resource('conpherence-widget-pane-css');
|
||||
Javelin::initBehavior(
|
||||
'conpherence-widget-pane',
|
||||
array(
|
||||
'widgetRegistery' => array(
|
||||
'widgets-files' => 1,
|
||||
'widgets-tasks' => 1,
|
||||
'widgets-calendar' => 1,
|
||||
)
|
||||
)
|
||||
);
|
||||
|
||||
$conpherence = $this->getConpherence();
|
||||
|
||||
$widgets = phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'widgets-header'
|
||||
),
|
||||
javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'sigil' => 'conpherence-change-widget',
|
||||
'meta' => array('widget' => 'widgets-files')
|
||||
),
|
||||
pht('Files')
|
||||
).' | '.
|
||||
javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'sigil' => 'conpherence-change-widget',
|
||||
'meta' => array('widget' => 'widgets-tasks')
|
||||
),
|
||||
pht('Tasks')
|
||||
).' | '.
|
||||
javelin_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'sigil' => 'conpherence-change-widget',
|
||||
'meta' => array('widget' => 'widgets-calendar')
|
||||
),
|
||||
pht('Calendar')
|
||||
)
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'widgets-body',
|
||||
'id' => 'widgets-files',
|
||||
'style' => 'display: none;'
|
||||
),
|
||||
$this->renderFilesWidgetPaneContent()
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'widgets-body',
|
||||
'id' => 'widgets-tasks',
|
||||
),
|
||||
$this->renderTaskWidgetPaneContent()
|
||||
).
|
||||
phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => 'widgets-body',
|
||||
'id' => 'widgets-calendar',
|
||||
'style' => 'display: none;'
|
||||
),
|
||||
$this->renderCalendarWidgetPaneContent()
|
||||
);
|
||||
|
||||
return array('widgets' => $widgets);
|
||||
}
|
||||
|
||||
private function renderFilesWidgetPaneContent() {
|
||||
$conpherence = $this->getConpherence();
|
||||
$widget_data = $conpherence->getWidgetData();
|
||||
$files = $widget_data['files'];
|
||||
|
||||
$table_data = array();
|
||||
foreach ($files as $file) {
|
||||
$thumb = $file->getThumb60x45URI();
|
||||
$table_data[] = array(
|
||||
phutil_render_tag(
|
||||
'img',
|
||||
array(
|
||||
'src' => $thumb
|
||||
),
|
||||
''
|
||||
),
|
||||
$file->getName()
|
||||
);
|
||||
}
|
||||
$header = id(new PhabricatorHeaderView())
|
||||
->setHeader(pht('Attached Files'));
|
||||
$table = id(new AphrontTableView($table_data))
|
||||
->setNoDataString(pht('No files attached to conpherence.'))
|
||||
->setHeaders(array('', pht('Name')))
|
||||
->setColumnClasses(array('', 'wide'));
|
||||
return $header->render() . $table->render();
|
||||
}
|
||||
|
||||
private function renderTaskWidgetPaneContent() {
|
||||
$conpherence = $this->getConpherence();
|
||||
$widget_data = $conpherence->getWidgetData();
|
||||
$tasks = $widget_data['tasks'];
|
||||
$priority_map = ManiphestTaskPriority::getTaskPriorityMap();
|
||||
$handles = $conpherence->getHandles();
|
||||
$content = array();
|
||||
foreach ($tasks as $owner_phid => $actual_tasks) {
|
||||
$handle = $handles[$owner_phid];
|
||||
$content[] = id(new PhabricatorHeaderView())
|
||||
->setHeader($handle->getName())
|
||||
->render();
|
||||
$actual_tasks = msort($actual_tasks, 'getPriority');
|
||||
$actual_tasks = array_reverse($actual_tasks);
|
||||
$data = array();
|
||||
foreach ($actual_tasks as $task) {
|
||||
$data[] = array(
|
||||
idx($priority_map, $task->getPriority(), pht('???')),
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => '/T'.$task->getID()
|
||||
),
|
||||
phutil_escape_html($task->getTitle())
|
||||
)
|
||||
);
|
||||
}
|
||||
$table = id(new AphrontTableView($data))
|
||||
->setNoDataString(pht('No open tasks.'))
|
||||
->setHeaders(array(pht('Pri'), pht('Title')))
|
||||
->setColumnClasses(array('', 'wide'));
|
||||
$content[] = $table->render();
|
||||
}
|
||||
return implode('', $content);
|
||||
}
|
||||
|
||||
private function renderCalendarWidgetPaneContent() {
|
||||
$header = id(new PhabricatorHeaderView())
|
||||
->setHeader(pht('Calendar'));
|
||||
return $header->render() . 'TODO';
|
||||
}
|
||||
|
||||
}
|
175
src/applications/conpherence/editor/ConpherenceEditor.php
Normal file
175
src/applications/conpherence/editor/ConpherenceEditor.php
Normal file
|
@ -0,0 +1,175 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
||||
|
||||
public function getTransactionTypes() {
|
||||
$types = parent::getTransactionTypes();
|
||||
|
||||
$types[] = PhabricatorTransactions::TYPE_COMMENT;
|
||||
|
||||
$types[] = ConpherenceTransactionType::TYPE_TITLE;
|
||||
$types[] = ConpherenceTransactionType::TYPE_PICTURE;
|
||||
$types[] = ConpherenceTransactionType::TYPE_PARTICIPANTS;
|
||||
$types[] = ConpherenceTransactionType::TYPE_FILES;
|
||||
|
||||
return $types;
|
||||
}
|
||||
|
||||
protected function getCustomTransactionOldValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
return $object->getTitle();
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
return $object->getImagePHID();
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
return $object->getParticipantPHIDs();
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
return $object->getFilePHIDs();
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCustomTransactionNewValue(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
return $xaction->getNewValue();
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
return $this->getPHIDTransactionNewValue($xaction);
|
||||
}
|
||||
}
|
||||
|
||||
protected function applyCustomInternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
$object->setTitle($xaction->getNewValue());
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
$object->setImagePHID($xaction->getNewValue());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* For now this only supports adding more files and participants.
|
||||
*/
|
||||
protected function applyCustomExternalTransaction(
|
||||
PhabricatorLiskDAO $object,
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
switch ($xaction->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
$editor = id(new PhabricatorEdgeEditor())
|
||||
->setActor($this->getActor());
|
||||
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE;
|
||||
foreach ($xaction->getNewValue() as $file_phid) {
|
||||
$editor->addEdge(
|
||||
$object->getPHID(),
|
||||
$edge_type,
|
||||
$file_phid
|
||||
);
|
||||
}
|
||||
$editor->save();
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
foreach ($xaction->getNewValue() as $participant) {
|
||||
if ($participant == $this->getActor()->getPHID()) {
|
||||
$status = ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
} else {
|
||||
$status = ConpherenceParticipationStatus::BEHIND;
|
||||
}
|
||||
id(new ConpherenceParticipant())
|
||||
->setConpherencePHID($object->getPHID())
|
||||
->setParticipantPHID($participant)
|
||||
->setParticipationStatus($status)
|
||||
->setDateTouched(time())
|
||||
->setBehindTransactionPHID($xaction->getPHID())
|
||||
->save();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function mergeTransactions(
|
||||
PhabricatorApplicationTransaction $u,
|
||||
PhabricatorApplicationTransaction $v) {
|
||||
|
||||
$type = $u->getTransactionType();
|
||||
switch ($type) {
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
return $v;
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
return $this->mergePHIDTransactions($u, $v);
|
||||
}
|
||||
|
||||
return parent::mergeTransactions($u, $v);
|
||||
}
|
||||
|
||||
protected function supportsMail() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/* TODO
|
||||
|
||||
protected function buildReplyHandler(PhabricatorLiskDAO $object) {
|
||||
return id(new ConpherenceReplyHandler())
|
||||
->setMailReceiver($object);
|
||||
}
|
||||
|
||||
protected function buildMailTemplate(PhabricatorLiskDAO $object) {
|
||||
$id = $object->getID();
|
||||
$title = $object->getTitle();
|
||||
$phid = $object->getPHID();
|
||||
$original_name = $object->getOriginalName();
|
||||
|
||||
return id(new PhabricatorMetaMTAMail())
|
||||
->setSubject("C{$id}: {$title}")
|
||||
->addHeader('Thread-Topic', "C{$id}: {$phid}");
|
||||
}
|
||||
|
||||
protected function getMailTo(PhabricatorLiskDAO $object) {
|
||||
$participants = $object->getParticipants();
|
||||
$participants[$this->requireActor()->getPHID()] = true;
|
||||
return array_keys($participants);
|
||||
}
|
||||
|
||||
protected function buildMailBody(
|
||||
PhabricatorLiskDAO $object,
|
||||
array $xactions) {
|
||||
|
||||
$body = parent::buildMailBody($object, $xactions);
|
||||
$body->addTextSection(
|
||||
pht('CONPHERENCE DETAIL'),
|
||||
PhabricatorEnv::getProductionURI('/conpherence/'.$object->getID().'/'));
|
||||
|
||||
return $body;
|
||||
}
|
||||
|
||||
protected function getMailSubjectPrefix() {
|
||||
return PhabricatorEnv::getEnvConfig('metamta.conpherence.subject-prefix');
|
||||
}
|
||||
*/
|
||||
|
||||
protected function supportsFeed() {
|
||||
return false;
|
||||
}
|
||||
|
||||
protected function supportsSearch() {
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceParticipantQuery
|
||||
extends PhabricatorOffsetPagedQuery {
|
||||
|
||||
private $conpherencePHIDs;
|
||||
private $participantPHIDs;
|
||||
private $dateTouched;
|
||||
private $dateTouchedSort;
|
||||
private $participationStatus;
|
||||
|
||||
public function withConpherencePHIDs(array $phids) {
|
||||
$this->conpherencePHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withParticipantPHIDs(array $phids) {
|
||||
$this->participantPHIDs = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withDateTouched($date, $sort = null) {
|
||||
$this->dateTouched = $date;
|
||||
$this->dateTouchedSort = $sort ? $sort : '<';
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withParticipationStatus($participation_status) {
|
||||
$this->participationStatus = $participation_status;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$table = new ConpherenceParticipant();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT * FROM %T participant %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
$participants = $table->loadAllFromArray($data);
|
||||
|
||||
$participants = mpull($participants, null, 'getConpherencePHID');
|
||||
|
||||
return $participants;
|
||||
}
|
||||
|
||||
private function buildWhereClause($conn_r) {
|
||||
$where = array();
|
||||
|
||||
if ($this->conpherencePHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'conpherencePHID IN (%Ls)',
|
||||
$this->conpherencePHIDs);
|
||||
}
|
||||
|
||||
if ($this->participantPHIDs) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'participantPHID IN (%Ls)',
|
||||
$this->participantPHIDs);
|
||||
}
|
||||
|
||||
if ($this->participationStatus !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'participationStatus = %d',
|
||||
$this->participationStatus
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->dateTouched) {
|
||||
if ($this->dateTouchedSort) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'dateTouched %Q %d',
|
||||
$this->dateTouchedSort,
|
||||
$this->dateTouched
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function buildOrderClause(AphrontDatabaseConnection $conn_r) {
|
||||
return 'ORDER BY dateTouched DESC';
|
||||
}
|
||||
|
||||
}
|
182
src/applications/conpherence/query/ConpherenceThreadQuery.php
Normal file
182
src/applications/conpherence/query/ConpherenceThreadQuery.php
Normal file
|
@ -0,0 +1,182 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceThreadQuery
|
||||
extends PhabricatorCursorPagedPolicyAwareQuery {
|
||||
|
||||
private $phids;
|
||||
private $ids;
|
||||
private $needWidgetData;
|
||||
|
||||
public function needWidgetData($need_widget_data) {
|
||||
$this->needWidgetData = $need_widget_data;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withPHIDs(array $phids) {
|
||||
$this->phids = $phids;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function loadPage() {
|
||||
$table = new ConpherenceThread();
|
||||
$conn_r = $table->establishConnection('r');
|
||||
|
||||
$data = queryfx_all(
|
||||
$conn_r,
|
||||
'SELECT conpherence_thread.* FROM %T conpherence_thread %Q %Q %Q',
|
||||
$table->getTableName(),
|
||||
$this->buildWhereClause($conn_r),
|
||||
$this->buildOrderClause($conn_r),
|
||||
$this->buildLimitClause($conn_r));
|
||||
|
||||
$conpherences = $table->loadAllFromArray($data);
|
||||
|
||||
if ($conpherences) {
|
||||
$conpherences = mpull($conpherences, null, 'getPHID');
|
||||
$this->loadParticipants($conpherences);
|
||||
$this->loadTransactionsAndHandles($conpherences);
|
||||
$this->loadFilePHIDs($conpherences);
|
||||
if ($this->needWidgetData) {
|
||||
$this->loadWidgetData($conpherences);
|
||||
}
|
||||
}
|
||||
|
||||
return $conpherences;
|
||||
}
|
||||
|
||||
protected function buildWhereClause($conn_r) {
|
||||
$where = array();
|
||||
|
||||
$where[] = $this->buildPagingClause($conn_r);
|
||||
|
||||
if ($this->ids) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'id IN (%Ld)',
|
||||
$this->ids);
|
||||
}
|
||||
|
||||
if ($this->phids) {
|
||||
$where[] = qsprintf(
|
||||
$conn_r,
|
||||
'phid IN (%Ls)',
|
||||
$this->phids);
|
||||
}
|
||||
|
||||
return $this->formatWhereClause($where);
|
||||
}
|
||||
|
||||
private function loadParticipants(array $conpherences) {
|
||||
$participants = id(new ConpherenceParticipant())
|
||||
->loadAllWhere('conpherencePHID IN (%Ls)', array_keys($conpherences));
|
||||
$map = mgroup($participants, 'getConpherencePHID');
|
||||
foreach ($map as $conpherence_phid => $conpherence_participants) {
|
||||
$current_conpherence = $conpherences[$conpherence_phid];
|
||||
$conpherence_participants = mpull(
|
||||
$conpherence_participants,
|
||||
null,
|
||||
'getParticipantPHID'
|
||||
);
|
||||
$current_conpherence->attachParticipants($conpherence_participants);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function loadTransactionsAndHandles(array $conpherences) {
|
||||
$transactions = id(new ConpherenceTransactionQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withObjectPHIDs(array_keys($conpherences))
|
||||
->needHandles(true)
|
||||
->loadPage();
|
||||
$transactions = mgroup($transactions, 'getObjectPHID');
|
||||
foreach ($conpherences as $phid => $conpherence) {
|
||||
$current_transactions = $transactions[$phid];
|
||||
$handles = array();
|
||||
foreach ($current_transactions as $transaction) {
|
||||
$handles += $transaction->getHandles();
|
||||
}
|
||||
$conpherence->attachHandles($handles);
|
||||
$conpherence->attachTransactions($transactions[$phid]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function loadFilePHIDs(array $conpherences) {
|
||||
$edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE;
|
||||
$file_edges = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(array_keys($conpherences))
|
||||
->withEdgeTypes(array($edge_type))
|
||||
->execute();
|
||||
foreach ($file_edges as $conpherence_phid => $data) {
|
||||
$conpherence = $conpherences[$conpherence_phid];
|
||||
$conpherence->attachFilePHIDs(array_keys($data[$edge_type]));
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function loadWidgetData(array $conpherences) {
|
||||
$participant_phids = array();
|
||||
$file_phids = array();
|
||||
foreach ($conpherences as $conpherence) {
|
||||
$participant_phids[] = array_keys($conpherence->getParticipants());
|
||||
$file_phids[] = $conpherence->getFilePHIDs();
|
||||
}
|
||||
$participant_phids = array_mergev($participant_phids);
|
||||
$file_phids = array_mergev($file_phids);
|
||||
|
||||
// open tasks of all participants
|
||||
$tasks = id(new ManiphestTaskQuery())
|
||||
->withOwners($participant_phids)
|
||||
->withStatus(ManiphestTaskQuery::STATUS_OPEN)
|
||||
->execute();
|
||||
$tasks = mgroup($tasks, 'getOwnerPHID');
|
||||
|
||||
// statuses of everyone currently in the conpherence
|
||||
// until the beginning of the next work week.
|
||||
// NOTE: this is a bit boring on the weekends.
|
||||
$end_of_week = phabricator_format_local_time(
|
||||
strtotime('Monday midnight'),
|
||||
$this->getViewer(),
|
||||
'U'
|
||||
);
|
||||
$statuses = id(new PhabricatorUserStatus())
|
||||
->loadAllWhere(
|
||||
'userPHID in (%Ls) AND dateTo <= %d',
|
||||
$participant_phids,
|
||||
$end_of_week
|
||||
);
|
||||
$statuses = mgroup($statuses, 'getUserPHID');
|
||||
|
||||
// attached files
|
||||
$files = array();
|
||||
if ($file_phids) {
|
||||
$files = id(new PhabricatorFileQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withPHIDs($file_phids)
|
||||
->execute();
|
||||
$files = mpull($files, null, 'getPHID');
|
||||
}
|
||||
|
||||
foreach ($conpherences as $phid => $conpherence) {
|
||||
$participant_phids = array_keys($conpherence->getParticipants());
|
||||
$widget_data = array(
|
||||
'tasks' => array_select_keys($tasks, $participant_phids),
|
||||
'statuses' => array_select_keys($statuses, $participant_phids),
|
||||
'files' => array_select_keys($files, $conpherence->getFilePHIDs()),
|
||||
);
|
||||
$conpherence->attachWidgetData($widget_data);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceTransactionQuery
|
||||
extends PhabricatorApplicationTransactionQuery {
|
||||
|
||||
protected function getTemplateApplicationTransaction() {
|
||||
return new ConpherenceTransaction();
|
||||
}
|
||||
|
||||
}
|
12
src/applications/conpherence/storage/ConpherenceDAO.php
Normal file
12
src/applications/conpherence/storage/ConpherenceDAO.php
Normal file
|
@ -0,0 +1,12 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
abstract class ConpherenceDAO extends PhabricatorLiskDAO {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'conpherence';
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceParticipant extends ConpherenceDAO {
|
||||
|
||||
protected $participantPHID;
|
||||
protected $conpherencePHID;
|
||||
protected $participationStatus;
|
||||
protected $behindTransactionPHID;
|
||||
protected $dateTouched;
|
||||
|
||||
public function markUpToDate() {
|
||||
if (!$this->isUpToDate()) {
|
||||
$this->setParticipationStatus(ConpherenceParticipationStatus::UP_TO_DATE);
|
||||
$this->save();
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isUpToDate() {
|
||||
return $this->getParticipationStatus() ==
|
||||
ConpherenceParticipationStatus::UP_TO_DATE;
|
||||
}
|
||||
|
||||
}
|
237
src/applications/conpherence/storage/ConpherenceThread.php
Normal file
237
src/applications/conpherence/storage/ConpherenceThread.php
Normal file
|
@ -0,0 +1,237 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceThread extends ConpherenceDAO
|
||||
implements PhabricatorPolicyInterface {
|
||||
|
||||
protected $id;
|
||||
protected $phid;
|
||||
protected $title;
|
||||
protected $imagePHID;
|
||||
protected $mailKey;
|
||||
|
||||
private $participants;
|
||||
private $transactions;
|
||||
private $handles;
|
||||
private $filePHIDs;
|
||||
private $widgetData;
|
||||
|
||||
public function getConfiguration() {
|
||||
return array(
|
||||
self::CONFIG_AUX_PHID => true,
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
return PhabricatorPHID::generateNewPHID(
|
||||
PhabricatorPHIDConstants::PHID_TYPE_CONP);
|
||||
}
|
||||
|
||||
public function save() {
|
||||
if (!$this->getMailKey()) {
|
||||
$this->setMailKey(Filesystem::readRandomCharacters(20));
|
||||
}
|
||||
return parent::save();
|
||||
}
|
||||
|
||||
public function attachParticipants(array $participants) {
|
||||
assert_instances_of($participants, 'ConpherenceParticipant');
|
||||
$this->participants = $participants;
|
||||
return $this;
|
||||
}
|
||||
public function getParticipants() {
|
||||
if ($this->participants === null) {
|
||||
throw new Exception(
|
||||
'You must attachParticipants first!'
|
||||
);
|
||||
}
|
||||
return $this->participants;
|
||||
}
|
||||
public function getParticipant($phid) {
|
||||
$participants = $this->getParticipants();
|
||||
return $participants[$phid];
|
||||
}
|
||||
public function getParticipantPHIDs() {
|
||||
$participants = $this->getParticipants();
|
||||
return array_keys($participants);
|
||||
}
|
||||
|
||||
public function attachHandles(array $handles) {
|
||||
assert_instances_of($handles, 'PhabricatorObjectHandle');
|
||||
$this->handles = $handles;
|
||||
return $this;
|
||||
}
|
||||
public function getHandles() {
|
||||
if ($this->handles === null) {
|
||||
throw new Exception(
|
||||
'You must attachHandles first!'
|
||||
);
|
||||
}
|
||||
return $this->handles;
|
||||
}
|
||||
|
||||
public function attachTransactions(array $transactions) {
|
||||
assert_instances_of($transactions, 'ConpherenceTransaction');
|
||||
$this->transactions = $transactions;
|
||||
return $this;
|
||||
}
|
||||
public function getTransactions() {
|
||||
if ($this->transactions === null) {
|
||||
throw new Exception(
|
||||
'You must attachTransactions first!'
|
||||
);
|
||||
}
|
||||
return $this->transactions;
|
||||
}
|
||||
|
||||
public function attachFilePHIDs(array $file_phids) {
|
||||
$this->filePHIDs = $file_phids;
|
||||
return $this;
|
||||
}
|
||||
public function getFilePHIDs() {
|
||||
if ($this->filePHIDs === null) {
|
||||
throw new Exception(
|
||||
'You must attachFilePHIDs first!'
|
||||
);
|
||||
}
|
||||
return $this->filePHIDs;
|
||||
}
|
||||
|
||||
public function attachWidgetData(array $widget_data) {
|
||||
$this->widgetData = $widget_data;
|
||||
return $this;
|
||||
}
|
||||
public function getWidgetData() {
|
||||
if ($this->widgetData === null) {
|
||||
throw new Exception(
|
||||
'You must attachWidgetData first!'
|
||||
);
|
||||
}
|
||||
return $this->widgetData;
|
||||
}
|
||||
|
||||
public function loadImageURI() {
|
||||
$src_phid = $this->getImagePHID();
|
||||
|
||||
if ($src_phid) {
|
||||
$file = id(new PhabricatorFile())->loadOneWhere('phid = %s', $src_phid);
|
||||
if ($file) {
|
||||
return $file->getBestURI();
|
||||
}
|
||||
}
|
||||
|
||||
return PhabricatorUser::getDefaultProfileImageURI();
|
||||
}
|
||||
|
||||
public function getDisplayData(PhabricatorUser $user) {
|
||||
$transactions = $this->getTransactions();
|
||||
$latest_transaction = end($transactions);
|
||||
$latest_participant = $latest_transaction->getAuthorPHID();
|
||||
$handles = $this->getHandles();
|
||||
$latest_handle = $handles[$latest_participant];
|
||||
if ($this->getImagePHID()) {
|
||||
$img_src = $this->loadImageURI();
|
||||
} else {
|
||||
$img_src = $latest_handle->getImageURI();
|
||||
}
|
||||
$title = $this->getTitle();
|
||||
if (!$title) {
|
||||
$title = $latest_handle->getName();
|
||||
unset($handles[$latest_participant]);
|
||||
}
|
||||
unset($handles[$user->getPHID()]);
|
||||
|
||||
$subtitle = '';
|
||||
$count = 0;
|
||||
$final = false;
|
||||
foreach ($handles as $handle) {
|
||||
if ($handle->getType() != PhabricatorPHIDConstants::PHID_TYPE_USER) {
|
||||
continue;
|
||||
}
|
||||
if ($subtitle) {
|
||||
if ($final) {
|
||||
$subtitle .= '...';
|
||||
break;
|
||||
} else {
|
||||
$subtitle .= ', ';
|
||||
}
|
||||
}
|
||||
$subtitle .= $handle->getName();
|
||||
$count++;
|
||||
$final = $count == 3;
|
||||
}
|
||||
|
||||
$participants = $this->getParticipants();
|
||||
$user_participation = $participants[$user->getPHID()];
|
||||
$unread_count = 0;
|
||||
$max_count = 10;
|
||||
$snippet = null;
|
||||
if (!$user_participation->isUpToDate()) {
|
||||
$behind_transaction_phid =
|
||||
$user_participation->getBehindTransactionPHID();
|
||||
} else {
|
||||
$behind_transaction_phid = null;
|
||||
}
|
||||
foreach (array_reverse($transactions) as $transaction) {
|
||||
switch ($transaction->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
continue 2;
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
if ($snippet === null) {
|
||||
$snippet = phutil_utf8_shorten(
|
||||
$transaction->getComment()->getContent(),
|
||||
48
|
||||
);
|
||||
}
|
||||
// fallthrough intentionally here
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
default:
|
||||
if ($behind_transaction_phid &&
|
||||
$transaction->getPHID() != $behind_transaction_phid) {
|
||||
$unread_count++;
|
||||
}
|
||||
if ($unread_count > $max_count) {
|
||||
break 2;
|
||||
}
|
||||
break;
|
||||
}
|
||||
if ($snippet && !$behind_transaction_phid) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if ($unread_count > $max_count) {
|
||||
$unread_count = $max_count.'+';
|
||||
}
|
||||
|
||||
return array(
|
||||
'title' => $title,
|
||||
'subtitle' => $subtitle,
|
||||
'unread_count' => $unread_count,
|
||||
'epoch' => $latest_transaction->getDateCreated(),
|
||||
'image' => $img_src,
|
||||
'snippet' => $snippet,
|
||||
);
|
||||
}
|
||||
|
||||
/* -( PhabricatorPolicyInterface Implementation )-------------------------- */
|
||||
public function getCapabilities() {
|
||||
return array(
|
||||
PhabricatorPolicyCapability::CAN_VIEW,
|
||||
PhabricatorPolicyCapability::CAN_EDIT,
|
||||
);
|
||||
}
|
||||
|
||||
public function getPolicy($capability) {
|
||||
return PhabricatorPolicies::POLICY_NOONE;
|
||||
}
|
||||
|
||||
public function hasAutomaticCapability($capability, PhabricatorUser $user) {
|
||||
$participants = $this->getParticipants();
|
||||
return isset($participants[$user->getPHID()]);
|
||||
}
|
||||
|
||||
}
|
124
src/applications/conpherence/storage/ConpherenceTransaction.php
Normal file
124
src/applications/conpherence/storage/ConpherenceTransaction.php
Normal file
|
@ -0,0 +1,124 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceTransaction extends PhabricatorApplicationTransaction {
|
||||
|
||||
public function getApplicationName() {
|
||||
return 'conpherence';
|
||||
}
|
||||
|
||||
public function getApplicationTransactionType() {
|
||||
return PhabricatorPHIDConstants::PHID_TYPE_CONP;
|
||||
}
|
||||
|
||||
public function getApplicationTransactionCommentObject() {
|
||||
return new ConpherenceTransactionComment();
|
||||
}
|
||||
|
||||
public function getApplicationObjectTypeName() {
|
||||
return pht('conpherence');
|
||||
}
|
||||
|
||||
public function shouldHide() {
|
||||
$old = $this->getOldValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
return ($old === null);
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::shouldHide();
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$author_phid = $this->getAuthorPHID();
|
||||
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
||||
switch ($this->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
if ($old) {
|
||||
$title = pht(
|
||||
'%s renamed this conpherence from "%s" to "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
phutil_escape_html($old),
|
||||
phutil_escape_html($new));
|
||||
} else {
|
||||
$title = pht(
|
||||
'%s named this conpherence "%s".',
|
||||
$this->renderHandleLink($author_phid),
|
||||
phutil_escape_html($new));
|
||||
}
|
||||
return $title;
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
return pht(
|
||||
'%s updated the conpherence files.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
return pht(
|
||||
'%s updated the conpherence image.',
|
||||
$this->renderHandleLink($author_phid));
|
||||
case ConpherenceTransactionType::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 partipant(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 ConpherenceTransactionType::TYPE_PICTURE:
|
||||
$phids[] = $new;
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
$phids = array_merge($phids, $this->getOldValue());
|
||||
$phids = array_merge($phids, $this->getNewValue());
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return $phids;
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceTransactionComment
|
||||
extends PhabricatorApplicationTransactionComment {
|
||||
|
||||
protected $conpherencePHID;
|
||||
|
||||
public function getApplicationTransactionObject() {
|
||||
return new ConpherenceTransaction();
|
||||
}
|
||||
|
||||
}
|
144
src/applications/conpherence/view/ConpherenceMenuItemView.php
Normal file
144
src/applications/conpherence/view/ConpherenceMenuItemView.php
Normal file
|
@ -0,0 +1,144 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceMenuItemView extends AphrontTagView {
|
||||
|
||||
private $title;
|
||||
private $subtitle;
|
||||
private $imageURI;
|
||||
private $href;
|
||||
private $messageText;
|
||||
private $epoch;
|
||||
private $unreadCount;
|
||||
|
||||
public function setUnreadCount($unread_count) {
|
||||
$this->unreadCount = $unread_count;
|
||||
return $this;
|
||||
}
|
||||
public function getUnreadCount() {
|
||||
return $this->unreadCount;
|
||||
}
|
||||
|
||||
public function setMessageText($message_text) {
|
||||
$this->messageText = $message_text;
|
||||
return $this;
|
||||
}
|
||||
public function getMessageText() {
|
||||
return $this->messageText;
|
||||
}
|
||||
|
||||
public function setEpoch($epoch) {
|
||||
$this->epoch = $epoch;
|
||||
return $this;
|
||||
}
|
||||
public function getEpoch() {
|
||||
return $this->epoch;
|
||||
}
|
||||
|
||||
public function setHref($href) {
|
||||
$this->href = $href;
|
||||
return $this;
|
||||
}
|
||||
public function getHref() {
|
||||
return $this->href;
|
||||
}
|
||||
|
||||
public function setImageURI($image_uri) {
|
||||
$this->imageURI = $image_uri;
|
||||
return $this;
|
||||
}
|
||||
public function getImageURI() {
|
||||
return $this->imageURI;
|
||||
}
|
||||
|
||||
public function setSubtitle($subtitle) {
|
||||
$this->subtitle = $subtitle;
|
||||
return $this;
|
||||
}
|
||||
public function getSubtitle() {
|
||||
return $this->subtitle;
|
||||
}
|
||||
|
||||
public function setTitle($title) {
|
||||
$this->title = $title;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
return $this->title;
|
||||
}
|
||||
|
||||
protected function getTagName() {
|
||||
return 'a';
|
||||
}
|
||||
|
||||
protected function getTagAttributes() {
|
||||
$classes = array('conpherence-menu-item-view');
|
||||
return array(
|
||||
'class' => $classes,
|
||||
'href' => $this->href,
|
||||
);
|
||||
}
|
||||
|
||||
protected function getTagContent() {
|
||||
$image = null;
|
||||
if ($this->imageURI) {
|
||||
$image = phutil_render_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'conpherence-menu-item-image',
|
||||
'style' => 'background-image: url('.$this->imageURI.');'
|
||||
),
|
||||
'');
|
||||
}
|
||||
$title = null;
|
||||
if ($this->title) {
|
||||
$title = phutil_render_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'conpherence-menu-item-title',
|
||||
),
|
||||
phutil_escape_html($this->title));
|
||||
}
|
||||
$subtitle = null;
|
||||
if ($this->subtitle) {
|
||||
$subtitle = phutil_render_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'conpherence-menu-item-subtitle',
|
||||
),
|
||||
phutil_escape_html($this->subtitle));
|
||||
}
|
||||
$message = null;
|
||||
if ($this->messageText) {
|
||||
$message = phutil_render_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'conpherence-menu-item-message-text'
|
||||
),
|
||||
phutil_escape_html($this->messageText));
|
||||
}
|
||||
$epoch = null;
|
||||
if ($this->epoch) {
|
||||
$epoch = phutil_render_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'conpherence-menu-item-date',
|
||||
),
|
||||
phabricator_relative_date($this->epoch, $this->user));
|
||||
}
|
||||
$unread_count = null;
|
||||
if ($this->unreadCount) {
|
||||
$unread_count = phutil_render_tag(
|
||||
'span',
|
||||
array(
|
||||
'class' => 'conpherence-menu-item-unread-count'
|
||||
),
|
||||
$this->unreadCount);
|
||||
}
|
||||
|
||||
return $image.$title.$subtitle.$message.$epoch.$unread_count;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,98 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* @group conpherence
|
||||
*/
|
||||
final class ConpherenceTransactionView extends AphrontView {
|
||||
|
||||
private $conpherenceTransaction;
|
||||
private $handles;
|
||||
|
||||
public function setHandles(array $handles) {
|
||||
assert_instances_of($handles, 'PhabricatorObjectHandle');
|
||||
$this->handles = $handles;
|
||||
return $this;
|
||||
}
|
||||
public function getHandles() {
|
||||
return $this->handles;
|
||||
}
|
||||
|
||||
public function setConpherenceTransaction(ConpherenceTransaction $tx) {
|
||||
$this->conpherenceTransaction = $tx;
|
||||
return $this;
|
||||
}
|
||||
private function getConpherenceTransaction() {
|
||||
return $this->conpherenceTransaction;
|
||||
}
|
||||
|
||||
public function render() {
|
||||
$transaction = $this->getConpherenceTransaction();
|
||||
$handles = $this->getHandles();
|
||||
$transaction->setHandles($handles);
|
||||
$author = $handles[$transaction->getAuthorPHID()];
|
||||
$transaction_view = id(new PhabricatorTransactionView())
|
||||
->setUser($this->getUser())
|
||||
->setEpoch($transaction->getDateCreated())
|
||||
->setContentSource($transaction->getContentSource());
|
||||
|
||||
$content_class = null;
|
||||
switch ($transaction->getTransactionType()) {
|
||||
case ConpherenceTransactionType::TYPE_TITLE:
|
||||
$content = $transaction->getTitle();
|
||||
$transaction_view->addClass('conpherence-edited');
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
$content = $transaction->getTitle();
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_PICTURE:
|
||||
$img = $transaction->getHandle($transaction->getNewValue());
|
||||
$content = $transaction->getTitle() .
|
||||
phutil_render_tag(
|
||||
'img',
|
||||
array(
|
||||
'src' => $img->getImageURI()
|
||||
)
|
||||
);
|
||||
$transaction_view->addClass('conpherence-edited');
|
||||
break;
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
$content = $transaction->getTitle();
|
||||
$transaction_view->addClass('conpherence-edited');
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_COMMENT:
|
||||
$comment = $transaction->getComment();
|
||||
$file_ids =
|
||||
PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles(
|
||||
array($comment->getContent())
|
||||
);
|
||||
$markup_field = ConpherenceTransactionComment::MARKUP_FIELD_COMMENT;
|
||||
$engine = id(new PhabricatorMarkupEngine())
|
||||
->setViewer($this->getUser());
|
||||
$engine->addObject(
|
||||
$comment,
|
||||
$markup_field
|
||||
);
|
||||
$engine->process();
|
||||
$content = $engine->getOutput(
|
||||
$comment,
|
||||
$markup_field
|
||||
);
|
||||
$content_class = 'conpherence-message phabricator-remarkup';
|
||||
$transaction_view
|
||||
->setImageURI($author->getImageURI())
|
||||
->setActions(array($author->renderLink()));
|
||||
break;
|
||||
}
|
||||
|
||||
$transaction_view
|
||||
->appendChild(phutil_render_tag(
|
||||
'div',
|
||||
array(
|
||||
'class' => $content_class
|
||||
),
|
||||
$content)
|
||||
);
|
||||
|
||||
return $transaction_view->render();
|
||||
}
|
||||
}
|
|
@ -39,13 +39,21 @@ final class PhabricatorPeopleProfileController
|
|||
}
|
||||
$username = phutil_escape_uri($user->getUserName());
|
||||
|
||||
$external_arrow = "\xE2\x86\x97";
|
||||
|
||||
$conpherence_uri =
|
||||
new PhutilURI('/conpherence/new/?participant='.$user->getPHID());
|
||||
$nav = new AphrontSideNavFilterView();
|
||||
$nav->setBaseURI(new PhutilURI('/p/'.$username.'/'));
|
||||
$nav->addFilter('feed', 'Feed');
|
||||
$nav->addMenuItem(
|
||||
id(new PhabricatorMenuItemView())
|
||||
->setName(pht('Conpherence').' '.$external_arrow)
|
||||
->setHref($conpherence_uri)
|
||||
);
|
||||
$nav->addFilter('about', 'About');
|
||||
$nav->addLabel('Activity');
|
||||
|
||||
$external_arrow = "\xE2\x86\x97";
|
||||
$nav->addFilter(
|
||||
null,
|
||||
"Revisions {$external_arrow}",
|
||||
|
|
|
@ -31,6 +31,7 @@ final class PhabricatorPHIDConstants {
|
|||
const PHID_TYPE_MOCK = 'MOCK';
|
||||
const PHID_TYPE_MCRO = 'MCRO';
|
||||
const PHID_TYPE_CONF = 'CONF';
|
||||
const PHID_TYPE_CONP = 'CONP';
|
||||
|
||||
const PHID_TYPE_XACT = 'XACT';
|
||||
const PHID_TYPE_XCMT = 'XCMT';
|
||||
|
|
|
@ -414,6 +414,9 @@ final class PhabricatorObjectHandleData {
|
|||
$handle->setName($file->getName());
|
||||
$handle->setURI($file->getBestURI());
|
||||
$handle->setComplete(true);
|
||||
if ($file->isViewableImage()) {
|
||||
$handle->setImageURI($file->getBestURI());
|
||||
}
|
||||
}
|
||||
$handles[$phid] = $handle;
|
||||
}
|
||||
|
|
|
@ -121,6 +121,15 @@ abstract class PhabricatorApplicationTransaction
|
|||
return $this->handles[$phid];
|
||||
}
|
||||
|
||||
public function getHandles() {
|
||||
if ($this->handles === null) {
|
||||
throw new Exception(
|
||||
'Transaction requires handles and it did not load them.'
|
||||
);
|
||||
}
|
||||
return $this->handles;
|
||||
}
|
||||
|
||||
protected function renderHandleLink($phid) {
|
||||
if ($this->renderingTarget == self::TARGET_HTML) {
|
||||
return $this->getHandle($phid)->renderLink();
|
||||
|
|
|
@ -39,6 +39,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|||
const TYPE_OBJECT_HAS_UNSUBSCRIBER = 23;
|
||||
const TYPE_UNSUBSCRIBED_FROM_OBJECT = 24;
|
||||
|
||||
const TYPE_OBJECT_HAS_FILE = 25;
|
||||
const TYPE_FILE_HAS_OBJECT = 26;
|
||||
|
||||
const TYPE_TEST_NO_CYCLE = 9000;
|
||||
|
||||
public static function getInverse($edge_type) {
|
||||
|
@ -78,6 +81,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|||
|
||||
self::TYPE_OBJECT_HAS_UNSUBSCRIBER => self::TYPE_UNSUBSCRIBED_FROM_OBJECT,
|
||||
self::TYPE_UNSUBSCRIBED_FROM_OBJECT => self::TYPE_OBJECT_HAS_UNSUBSCRIBER,
|
||||
|
||||
self::TYPE_OBJECT_HAS_FILE => self::TYPE_FILE_HAS_OBJECT,
|
||||
self::TYPE_FILE_HAS_OBJECT => self::TYPE_OBJECT_HAS_FILE,
|
||||
);
|
||||
|
||||
return idx($map, $edge_type);
|
||||
|
@ -109,6 +115,7 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
|
|||
PhabricatorPHIDConstants::PHID_TYPE_ANSW => 'PonderAnswer',
|
||||
PhabricatorPHIDConstants::PHID_TYPE_MOCK => 'PholioMock',
|
||||
PhabricatorPHIDConstants::PHID_TYPE_MCRO => 'PhabricatorFileImageMacro',
|
||||
PhabricatorPHIDConstants::PHID_TYPE_CONP => 'ConpherenceThread',
|
||||
|
||||
);
|
||||
|
||||
|
|
|
@ -193,6 +193,39 @@ abstract class PhabricatorBaseEnglishTranslation
|
|||
),
|
||||
),
|
||||
|
||||
'%s removed %d subscriber(s): %s.' => array(
|
||||
array(
|
||||
'%s removed a subscriber: %3$s.',
|
||||
'%s removed subscribers: %3$s.',
|
||||
),
|
||||
array(
|
||||
'%s removed a subscriber: %3$s.',
|
||||
'%s removed subscribers: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s added %d participant(s): %s.' => array(
|
||||
array(
|
||||
'%s added a participant: %3$s.',
|
||||
'%s added participants: %3$s.',
|
||||
),
|
||||
array(
|
||||
'%s added a participant: %3$s.',
|
||||
'%s added participants: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%s removed %d participant(s): %s.' => array(
|
||||
array(
|
||||
'%s removed a participant: %3$s.',
|
||||
'%s removed participants: %3$s.',
|
||||
),
|
||||
array(
|
||||
'%s removed a participant: %3$s.',
|
||||
'%s removed participants: %3$s.',
|
||||
),
|
||||
),
|
||||
|
||||
'%2$s Line(s)' => array(
|
||||
'%2$s Line',
|
||||
'%2$s Lines',
|
||||
|
|
|
@ -480,6 +480,22 @@ final class PhabricatorMarkupEngine {
|
|||
return $mentions;
|
||||
}
|
||||
|
||||
public static function extractFilePHIDsFromEmbeddedFiles(
|
||||
array $content_blocks) {
|
||||
$files = array();
|
||||
|
||||
$engine = self::newDifferentialMarkupEngine();
|
||||
|
||||
foreach ($content_blocks as $content_block) {
|
||||
$engine->markupText($content_block);
|
||||
$ids = $engine->getTextMetadata(
|
||||
PhabricatorRemarkupRuleEmbedFile::KEY_EMBED_FILE_PHIDS,
|
||||
array());
|
||||
$files += $ids;
|
||||
}
|
||||
|
||||
return $files;
|
||||
}
|
||||
|
||||
/**
|
||||
* Produce a corpus summary, in a way that shortens the underlying text
|
||||
|
|
|
@ -7,6 +7,7 @@ final class PhabricatorRemarkupRuleEmbedFile
|
|||
extends PhutilRemarkupRule {
|
||||
|
||||
const KEY_RULE_EMBED_FILE = 'rule.embed.file';
|
||||
const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids';
|
||||
|
||||
public function apply($text) {
|
||||
return preg_replace_callback(
|
||||
|
@ -87,6 +88,7 @@ final class PhabricatorRemarkupRuleEmbedFile
|
|||
return;
|
||||
}
|
||||
|
||||
$file_phids = array();
|
||||
foreach ($metadata as $phid => $bundles) {
|
||||
foreach ($bundles as $data) {
|
||||
|
||||
|
@ -159,7 +161,9 @@ final class PhabricatorRemarkupRuleEmbedFile
|
|||
|
||||
$engine->overwriteStoredText($data['token'], $embed);
|
||||
}
|
||||
$file_phids[] = $phid;
|
||||
}
|
||||
$engine->setTextMetadata(self::KEY_EMBED_FILE_PHIDS, $file_phids);
|
||||
$engine->setTextMetadata($metadata_key, array());
|
||||
}
|
||||
|
||||
|
|
|
@ -159,6 +159,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => 'db',
|
||||
'name' => 'pholio',
|
||||
),
|
||||
'db.conpherence' => array(
|
||||
'type' => 'db',
|
||||
'name' => 'conpherence',
|
||||
),
|
||||
'0000.legacy.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('0000.legacy.sql'),
|
||||
|
@ -1085,6 +1089,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20130103.filemetadata.sql'),
|
||||
),
|
||||
'20130111.conpherence.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20130111.conpherence.sql'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -128,6 +128,7 @@ final class AphrontDialogView extends AphrontView {
|
|||
'action' => $this->submitURI,
|
||||
'method' => 'post',
|
||||
'id' => $this->formID,
|
||||
'enctype' => $this->encType,
|
||||
);
|
||||
|
||||
$hidden_inputs = array();
|
||||
|
|
|
@ -12,6 +12,15 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
|
|||
private $height;
|
||||
private $readOnly;
|
||||
private $customClass;
|
||||
private $placeHolder;
|
||||
|
||||
public function setPlaceHolder($place_holder) {
|
||||
$this->placeHolder = $place_holder;
|
||||
return $this;
|
||||
}
|
||||
private function getPlaceHolder() {
|
||||
return $this->placeHolder;
|
||||
}
|
||||
|
||||
public function setHeight($height) {
|
||||
$this->height = $height;
|
||||
|
@ -55,12 +64,13 @@ class AphrontFormTextAreaControl extends AphrontFormControl {
|
|||
return phutil_render_tag(
|
||||
'textarea',
|
||||
array(
|
||||
'name' => $this->getName(),
|
||||
'disabled' => $this->getDisabled() ? 'disabled' : null,
|
||||
'readonly' => $this->getReadonly() ? 'readonly' : null,
|
||||
'class' => $classes,
|
||||
'style' => $this->getControlStyle(),
|
||||
'id' => $this->getID(),
|
||||
'name' => $this->getName(),
|
||||
'disabled' => $this->getDisabled() ? 'disabled' : null,
|
||||
'readonly' => $this->getReadonly() ? 'readonly' : null,
|
||||
'class' => $classes,
|
||||
'style' => $this->getControlStyle(),
|
||||
'id' => $this->getID(),
|
||||
'placeholder' => $this->getPlaceHolder(),
|
||||
),
|
||||
phutil_escape_html($this->getValue()));
|
||||
}
|
||||
|
|
|
@ -25,16 +25,25 @@ final class AphrontSideNavFilterView extends AphrontView {
|
|||
private $menu;
|
||||
private $crumbs;
|
||||
private $classes = array();
|
||||
private $menuID;
|
||||
|
||||
public function addClass($class) {
|
||||
$this->classes[] = $class;
|
||||
public function setMenuID($menu_id) {
|
||||
$this->menuID = $menu_id;
|
||||
return $this;
|
||||
}
|
||||
public function getMenuID() {
|
||||
return $this->menuID;
|
||||
}
|
||||
|
||||
public function __construct() {
|
||||
$this->menu = new PhabricatorMenuView();
|
||||
}
|
||||
|
||||
public function addClass($class) {
|
||||
$this->classes[] = $class;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public static function newFromMenu(PhabricatorMenuView $menu) {
|
||||
$object = new AphrontSideNavFilterView();
|
||||
$object->setBaseURI(new PhutilURI('/'));
|
||||
|
@ -205,7 +214,8 @@ final class AphrontSideNavFilterView extends AphrontView {
|
|||
'class' => 'phabricator-nav-local phabricator-side-menu',
|
||||
'id' => $local_id,
|
||||
),
|
||||
self::renderSingleView($this->menu));
|
||||
self::renderSingleView($this->menu->setID($this->getMenuID()))
|
||||
);
|
||||
}
|
||||
|
||||
$crumbs = null;
|
||||
|
|
|
@ -196,6 +196,7 @@ table.aphront-form-control-checkbox-layout th {
|
|||
}
|
||||
|
||||
.aphront-form-control-image .default-image {
|
||||
display: inline;
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
|
|
41
webroot/rsrc/css/application/conpherence/header-pane.css
Normal file
41
webroot/rsrc/css/application/conpherence/header-pane.css
Normal file
|
@ -0,0 +1,41 @@
|
|||
/**
|
||||
* @provides conpherence-header-pane-css
|
||||
*/
|
||||
|
||||
.conpherence-header-pane {
|
||||
border-bottom: 1px solid #ccc;
|
||||
background: #fff;
|
||||
height: 50px;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.conpherence-header-pane .edit {
|
||||
position: relative;
|
||||
float: right;
|
||||
margin: 16px 16px 0px 0px;
|
||||
}
|
||||
|
||||
.conpherence-header-pane .header-image {
|
||||
position: absolute;
|
||||
height: 50px;
|
||||
width: 50px;
|
||||
}
|
||||
|
||||
.conpherence-header-pane .title {
|
||||
position: relative;
|
||||
font-size: 16px;
|
||||
left: 62px;
|
||||
top: 6px;
|
||||
max-width: 80%;
|
||||
overflow-x: scroll;
|
||||
}
|
||||
|
||||
.conpherence-header-pane .subtitle {
|
||||
position: relative;
|
||||
left: 62px;
|
||||
top: 6px;
|
||||
color: #bfbfbf;
|
||||
max-width: 80%;
|
||||
}
|
||||
|
||||
|
121
webroot/rsrc/css/application/conpherence/menu.css
Normal file
121
webroot/rsrc/css/application/conpherence/menu.css
Normal file
|
@ -0,0 +1,121 @@
|
|||
/**
|
||||
* @provides conpherence-menu-css
|
||||
*/
|
||||
|
||||
.no-conpherences-menu-item {
|
||||
color: #ffffff;
|
||||
border-top: solid 1px #3B3D3E;
|
||||
padding: 20px 0px 20px 66px;
|
||||
}
|
||||
|
||||
.conpherence-menu .phabricator-nav-column-background,
|
||||
.conpherence-menu .phabricator-nav-local {
|
||||
width: 320px;
|
||||
}
|
||||
.conpherence-menu .phabricator-nav-local {
|
||||
top: 44px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
.conpherence-menu .phabricator-nav-drag {
|
||||
left: 320px;
|
||||
}
|
||||
|
||||
.device-desktop .conpherence-menu .phabricator-nav-content {
|
||||
margin-left: 320px !important;
|
||||
}
|
||||
|
||||
.conpherence-menu .phabricator-menu-view {
|
||||
overflow-x: hidden;
|
||||
overflow-y: scroll;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view {
|
||||
display: block;
|
||||
height: 70px;
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
position: relative;
|
||||
text-decoration: none;
|
||||
border-top: solid 1px #3B3D3E;
|
||||
border-bottom: solid 1px #1C1F21;
|
||||
border-right: 0;
|
||||
border-left: 2px solid transparent;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-selected {
|
||||
background: rgba(0, 0, 0, .6);
|
||||
border-left: 2px solid #66CCFF;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view:hover {
|
||||
background-image: url('/rsrc/image/texture/dark-menu-hover.png');
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-image {
|
||||
top: 6px;
|
||||
left: 6px;
|
||||
display: block;
|
||||
position: absolute;
|
||||
width: 50px;
|
||||
height: 50px;
|
||||
border: 4px solid rgb(29, 32, 34);
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-title {
|
||||
display: block;
|
||||
margin-top: 12px;
|
||||
margin-left: 70px;
|
||||
text-align: left;
|
||||
font-weight: bold;
|
||||
font-size: 12px;
|
||||
color: #ffffff;
|
||||
text-shadow: 0px 1px 1px #000000;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-subtitle {
|
||||
display: block;
|
||||
color: #bfbfbf;
|
||||
font-size: 11px;
|
||||
margin-top: 2px;
|
||||
margin-left: 70px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view
|
||||
.conpherence-menu-item-message-text {
|
||||
display: block;
|
||||
color: #66CCFF;
|
||||
font-size: 12px;
|
||||
margin-top: 4px;
|
||||
margin-left: 70px;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view
|
||||
.conpherence-menu-item-unread-count {
|
||||
position: absolute;
|
||||
left: 48px;
|
||||
top: 3px;
|
||||
background: #f00;
|
||||
border-radius: 10px;
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
padding: 1px 6px 2px;
|
||||
border: 1px solid #a00;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.conpherence-menu .hide-unread-count .conpherence-menu-item-unread-count,
|
||||
.conpherence-menu .conpherence-selected .conpherence-menu-item-unread-count {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-date {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 12px;
|
||||
color: white;
|
||||
font-size: 12px;
|
||||
}
|
95
webroot/rsrc/css/application/conpherence/message-pane.css
Normal file
95
webroot/rsrc/css/application/conpherence/message-pane.css
Normal file
|
@ -0,0 +1,95 @@
|
|||
/**
|
||||
* @provides conpherence-message-pane-css
|
||||
*/
|
||||
.conpherence-message-pane {
|
||||
position: fixed;
|
||||
left: 320px;
|
||||
top: 94px;
|
||||
min-width: 320px;
|
||||
width: auto;
|
||||
height: 100%;
|
||||
margin: 0px 320px 0px 0px;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .conpherence-messages {
|
||||
position: fixed;
|
||||
left: 320px;
|
||||
right: 321px;
|
||||
top: 94px;
|
||||
bottom: 225px;
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .phabricator-form-view {
|
||||
border-width: 0;
|
||||
background: none;
|
||||
height: 224px;
|
||||
padding: 0;
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 320px;
|
||||
right: 321px;
|
||||
}
|
||||
|
||||
.device-tablet
|
||||
.conpherence-message-pane .phabricator-form-view,
|
||||
.device-tablet
|
||||
.conpherence-message-pane .conpherence-messages,
|
||||
.device-tablet
|
||||
.conpherence-message-pane {
|
||||
left: 0px;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .aphront-form-input {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .phabricator-transaction-view {
|
||||
margin: 1em 1.25em 1em 1.25em;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .phabricator-transaction-detail {
|
||||
border-width: 0;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .phabricator-transaction-header {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .phabricator-transaction-info {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .phabricator-transaction-content {
|
||||
background: none;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .conpherence-edited .phabricator-transaction-content {
|
||||
font-size: 12px;
|
||||
padding: 0em 1em 0em 1em;
|
||||
margin-top: -4px;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .aphront-form-control {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .aphront-form-control-submit {
|
||||
margin-right: 8px;
|
||||
}
|
||||
.conpherence-message-pane .remarkup-assist-bar {
|
||||
border-width: 1px 0 0;
|
||||
border-color: #CCC;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .remarkup-assist-textarea {
|
||||
border-width: 1px 0 1px 0;
|
||||
border-color: #CCC;
|
||||
}
|
||||
|
||||
.conpherence-message-pane .remarkup-assist-textarea:focus {
|
||||
outline: none;
|
||||
}
|
||||
|
7
webroot/rsrc/css/application/conpherence/update.css
Normal file
7
webroot/rsrc/css/application/conpherence/update.css
Normal file
|
@ -0,0 +1,7 @@
|
|||
/**
|
||||
* @provides conpherence-update-css
|
||||
*/
|
||||
|
||||
.phabricator-standard-page-body .aphront-dialog-view {
|
||||
margin: 20px auto 0px auto;
|
||||
}
|
19
webroot/rsrc/css/application/conpherence/widget-pane.css
Normal file
19
webroot/rsrc/css/application/conpherence/widget-pane.css
Normal file
|
@ -0,0 +1,19 @@
|
|||
/**
|
||||
* @provides conpherence-widget-pane-css
|
||||
*/
|
||||
|
||||
.conpherence-widget-pane {
|
||||
position: fixed;
|
||||
right: 0px;
|
||||
top: 94px;
|
||||
width: 320px;
|
||||
height: 100%;
|
||||
border-width: 0 0 0 1px;
|
||||
border-color: #CCC;
|
||||
border-style: solid;
|
||||
}
|
||||
|
||||
.conpherence-widget-pane .aphront-form-input {
|
||||
margin: 0;
|
||||
width: 100%;
|
||||
}
|
20
webroot/rsrc/js/application/conpherence/behavior-init.js
Normal file
20
webroot/rsrc/js/application/conpherence/behavior-init.js
Normal file
|
@ -0,0 +1,20 @@
|
|||
/**
|
||||
* @provides javelin-behavior-conpherence-init
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* javelin-stratcom
|
||||
*/
|
||||
JX.behavior('conpherence-init', function(config) {
|
||||
// select the current message
|
||||
var selectedConpherence = false;
|
||||
if (config.selected_conpherence_id) {
|
||||
var selected = JX.$(config.selected_conpherence_id);
|
||||
JX.Stratcom.invoke(
|
||||
'conpherence-initial-selected',
|
||||
null,
|
||||
{ selected : selected }
|
||||
);
|
||||
selectedConpherence = true;
|
||||
}
|
||||
|
||||
});
|
96
webroot/rsrc/js/application/conpherence/behavior-menu.js
Normal file
96
webroot/rsrc/js/application/conpherence/behavior-menu.js
Normal file
|
@ -0,0 +1,96 @@
|
|||
/**
|
||||
* @provides javelin-behavior-conpherence-menu
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* javelin-workflow
|
||||
* javelin-util
|
||||
* javelin-stratcom
|
||||
* javelin-uri
|
||||
*/
|
||||
|
||||
JX.behavior('conpherence-menu', function(config) {
|
||||
|
||||
function onresponse(context, response) {
|
||||
var header = JX.$H(response.header);
|
||||
var messages = JX.$H(response.messages);
|
||||
var form = JX.$H(response.form);
|
||||
var widgets = JX.$H(response.widgets);
|
||||
var headerRoot = JX.$(config.header);
|
||||
var messagesRoot = JX.$(config.messages);
|
||||
var formRoot = JX.$(config.form_pane);
|
||||
var widgetsRoot = JX.$(config.widgets_pane);
|
||||
JX.DOM.setContent(headerRoot, header);
|
||||
JX.DOM.setContent(messagesRoot, messages);
|
||||
messagesRoot.scrollTop = messagesRoot.scrollHeight;
|
||||
JX.DOM.setContent(formRoot, form);
|
||||
JX.DOM.setContent(widgetsRoot, widgets);
|
||||
|
||||
for (var i = 0; i < context.parentNode.childNodes.length; i++) {
|
||||
var current = context.parentNode.childNodes[i];
|
||||
if (current.id == context.id) {
|
||||
JX.DOM.alterClass(current, 'conpherence-selected', true);
|
||||
JX.DOM.alterClass(current, 'hide-unread-count', true);
|
||||
} else {
|
||||
JX.DOM.alterClass(current, 'conpherence-selected', false);
|
||||
}
|
||||
}
|
||||
|
||||
// TODO - update the browser URI T2086
|
||||
|
||||
JX.Stratcom.invoke(
|
||||
'conpherence-selected-loaded',
|
||||
null,
|
||||
{}
|
||||
);
|
||||
}
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'conpherence-menu-click',
|
||||
function(e) {
|
||||
e.kill();
|
||||
var selected = e.getNode(['conpherence-menu-click']);
|
||||
if (config.fancy_ajax) {
|
||||
JX.Stratcom.invoke(
|
||||
'conpherence-selected',
|
||||
null,
|
||||
{ selected : selected }
|
||||
);
|
||||
} else {
|
||||
var data = JX.Stratcom.getData(selected);
|
||||
var uri = new JX.URI(config.base_uri + data.id + '/');
|
||||
uri.go();
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'conpherence-initial-selected',
|
||||
null,
|
||||
function(e) {
|
||||
var selected = e.getData().selected;
|
||||
e.kill();
|
||||
JX.Stratcom.invoke(
|
||||
'conpherence-selected',
|
||||
null,
|
||||
{ selected : selected }
|
||||
);
|
||||
}
|
||||
);
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'conpherence-selected',
|
||||
null,
|
||||
function(e) {
|
||||
|
||||
var selected = e.getData().selected;
|
||||
var data = JX.Stratcom.getData(selected);
|
||||
|
||||
var uri = config.base_uri + 'view/' + data.id + '/';
|
||||
new JX.Workflow(uri, {})
|
||||
.setHandler(JX.bind(null, onresponse, selected))
|
||||
.start();
|
||||
}
|
||||
);
|
||||
|
||||
});
|
|
@ -0,0 +1,26 @@
|
|||
/**
|
||||
* @provides javelin-behavior-conpherence-widget-pane
|
||||
* @requires javelin-behavior
|
||||
* javelin-dom
|
||||
* javelin-stratcom
|
||||
*/
|
||||
|
||||
JX.behavior('conpherence-widget-pane', function(config) {
|
||||
|
||||
JX.Stratcom.listen(
|
||||
'click',
|
||||
'conpherence-change-widget',
|
||||
function(e) {
|
||||
e.kill();
|
||||
var data = e.getNodeData('conpherence-change-widget');
|
||||
for (var widget in config.widgetRegistery) {
|
||||
if (widget == data.widget) {
|
||||
JX.$(widget).style.display = 'block';
|
||||
} else {
|
||||
JX.$(widget).style.display = 'none';
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
});
|
Loading…
Add table
Reference in a new issue