@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

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

+3054 -51
+83
resources/sql/patches/20130111.conpherence.sql
··· 1 + 2 + CREATE TABLE {$NAMESPACE}_conpherence.conpherence_thread ( 3 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 4 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 5 + title VARCHAR(255), 6 + imagePHID VARCHAR(64) COLLATE utf8_bin, 7 + mailKey VARCHAR(20) NOT NULL COLLATE utf8_bin, 8 + dateCreated INT UNSIGNED NOT NULL, 9 + dateModified INT UNSIGNED NOT NULL, 10 + UNIQUE KEY(phid) 11 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 12 + 13 + CREATE TABLE {$NAMESPACE}_conpherence.conpherence_participant ( 14 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 15 + participantPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 16 + conpherencePHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 17 + participationStatus INT UNSIGNED NOT NULL DEFAULT 0, 18 + dateTouched INT UNSIGNED NOT NULL, 19 + behindTransactionPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 20 + dateCreated INT UNSIGNED NOT NULL, 21 + dateModified INT UNSIGNED NOT NULL, 22 + UNIQUE KEY(conpherencePHID, participantPHID), 23 + KEY(participantPHID, participationStatus, dateTouched) 24 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 25 + 26 + CREATE TABLE {$NAMESPACE}_conpherence.edge ( 27 + src varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 28 + type varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 29 + dst varchar(64) CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 30 + dateCreated int(10) unsigned NOT NULL, 31 + seq int(10) unsigned NOT NULL, 32 + dataID int(10) unsigned DEFAULT NULL, 33 + PRIMARY KEY (src, type, dst), 34 + KEY src (src, type, dateCreated, seq) 35 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 36 + 37 + CREATE TABLE {$NAMESPACE}_conpherence.edgedata ( 38 + id int(10) unsigned NOT NULL AUTO_INCREMENT, 39 + data longtext CHARACTER SET utf8 COLLATE utf8_bin NOT NULL, 40 + PRIMARY KEY (id) 41 + ) ENGINE=InnoDB DEFAULT CHARSET=utf8; 42 + 43 + CREATE TABLE {$NAMESPACE}_conpherence.conpherence_transaction ( 44 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 45 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 46 + authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 47 + objectPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 48 + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 49 + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 50 + commentPHID VARCHAR(64) COLLATE utf8_bin, 51 + commentVersion INT UNSIGNED NOT NULL, 52 + transactionType VARCHAR(32) NOT NULL COLLATE utf8_bin, 53 + oldValue LONGTEXT NOT NULL COLLATE utf8_bin, 54 + newValue LONGTEXT NOT NULL COLLATE utf8_bin, 55 + contentSource LONGTEXT NOT NULL COLLATE utf8_bin, 56 + dateCreated INT UNSIGNED NOT NULL, 57 + dateModified INT UNSIGNED NOT NULL, 58 + UNIQUE KEY `key_phid` (phid), 59 + KEY `key_object` (objectPHID) 60 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 61 + 62 + CREATE TABLE {$NAMESPACE}_conpherence.conpherence_transaction_comment ( 63 + id INT UNSIGNED NOT NULL PRIMARY KEY AUTO_INCREMENT, 64 + phid VARCHAR(64) NOT NULL COLLATE utf8_bin, 65 + transactionPHID VARCHAR(64) COLLATE utf8_bin, 66 + authorPHID VARCHAR(64) NOT NULL COLLATE utf8_bin, 67 + viewPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 68 + editPolicy VARCHAR(64) NOT NULL COLLATE utf8_bin, 69 + commentVersion INT UNSIGNED NOT NULL, 70 + content LONGTEXT NOT NULL COLLATE utf8_bin, 71 + contentSource LONGTEXT NOT NULL COLLATE utf8_bin, 72 + isDeleted BOOL NOT NULL, 73 + dateCreated INT UNSIGNED NOT NULL, 74 + dateModified INT UNSIGNED NOT NULL, 75 + 76 + conpherencePHID VARCHAR(64) COLLATE utf8_bin, 77 + 78 + UNIQUE KEY `key_phid` (phid), 79 + UNIQUE KEY `key_version` (transactionPHID, commentVersion), 80 + UNIQUE KEY `key_draft` (authorPHID, conpherencePHID, transactionPHID) 81 + 82 + ) ENGINE=InnoDB, COLLATE utf8_general_ci; 83 +
+125 -41
src/__celerity_resource_map__.php
··· 618 618 ), 619 619 'aphront-form-view-css' => 620 620 array( 621 - 'uri' => '/res/45162273/rsrc/css/aphront/form-view.css', 621 + 'uri' => '/res/1e191b83/rsrc/css/aphront/form-view.css', 622 622 'type' => 'css', 623 623 'requires' => 624 624 array( ··· 724 724 array( 725 725 ), 726 726 'disk' => '/rsrc/css/application/config/config-options.css', 727 + ), 728 + 'conpherence-header-pane-css' => 729 + array( 730 + 'uri' => '/res/5a02bdbe/rsrc/css/application/conpherence/header-pane.css', 731 + 'type' => 'css', 732 + 'requires' => 733 + array( 734 + ), 735 + 'disk' => '/rsrc/css/application/conpherence/header-pane.css', 736 + ), 737 + 'conpherence-menu-css' => 738 + array( 739 + 'uri' => '/res/b893e529/rsrc/css/application/conpherence/menu.css', 740 + 'type' => 'css', 741 + 'requires' => 742 + array( 743 + ), 744 + 'disk' => '/rsrc/css/application/conpherence/menu.css', 745 + ), 746 + 'conpherence-message-pane-css' => 747 + array( 748 + 'uri' => '/res/32073bd5/rsrc/css/application/conpherence/message-pane.css', 749 + 'type' => 'css', 750 + 'requires' => 751 + array( 752 + ), 753 + 'disk' => '/rsrc/css/application/conpherence/message-pane.css', 754 + ), 755 + 'conpherence-update-css' => 756 + array( 757 + 'uri' => '/res/8e4757b5/rsrc/css/application/conpherence/update.css', 758 + 'type' => 'css', 759 + 'requires' => 760 + array( 761 + ), 762 + 'disk' => '/rsrc/css/application/conpherence/update.css', 763 + ), 764 + 'conpherence-widget-pane-css' => 765 + array( 766 + 'uri' => '/res/7af40cac/rsrc/css/application/conpherence/widget-pane.css', 767 + 'type' => 'css', 768 + 'requires' => 769 + array( 770 + ), 771 + 'disk' => '/rsrc/css/application/conpherence/widget-pane.css', 727 772 ), 728 773 'differential-changeset-view-css' => 729 774 array( ··· 1038 1083 3 => 'phabricator-shaped-request', 1039 1084 ), 1040 1085 'disk' => '/rsrc/js/application/diffusion/behavior-audit-preview.js', 1086 + ), 1087 + 'javelin-behavior-conpherence-init' => 1088 + array( 1089 + 'uri' => '/res/bd911b43/rsrc/js/application/conpherence/behavior-init.js', 1090 + 'type' => 'js', 1091 + 'requires' => 1092 + array( 1093 + 0 => 'javelin-behavior', 1094 + 1 => 'javelin-dom', 1095 + 2 => 'javelin-stratcom', 1096 + ), 1097 + 'disk' => '/rsrc/js/application/conpherence/behavior-init.js', 1098 + ), 1099 + 'javelin-behavior-conpherence-menu' => 1100 + array( 1101 + 'uri' => '/res/9d21fb86/rsrc/js/application/conpherence/behavior-menu.js', 1102 + 'type' => 'js', 1103 + 'requires' => 1104 + array( 1105 + 0 => 'javelin-behavior', 1106 + 1 => 'javelin-dom', 1107 + 2 => 'javelin-workflow', 1108 + 3 => 'javelin-util', 1109 + 4 => 'javelin-stratcom', 1110 + 5 => 'javelin-uri', 1111 + ), 1112 + 'disk' => '/rsrc/js/application/conpherence/behavior-menu.js', 1113 + ), 1114 + 'javelin-behavior-conpherence-widget-pane' => 1115 + array( 1116 + 'uri' => '/res/f3e0dbba/rsrc/js/application/conpherence/behavior-widget-pane.js', 1117 + 'type' => 'js', 1118 + 'requires' => 1119 + array( 1120 + 0 => 'javelin-behavior', 1121 + 1 => 'javelin-dom', 1122 + 2 => 'javelin-stratcom', 1123 + ), 1124 + 'disk' => '/rsrc/js/application/conpherence/behavior-widget-pane.js', 1041 1125 ), 1042 1126 'javelin-behavior-countdown-timer' => 1043 1127 array( ··· 3254 3338 ), array( 3255 3339 'packages' => 3256 3340 array( 3257 - 'd770a9ec' => 3341 + '8fd19266' => 3258 3342 array( 3259 3343 'name' => 'core.pkg.css', 3260 3344 'symbols' => ··· 3298 3382 36 => 'phabricator-object-item-list-view-css', 3299 3383 37 => 'global-drag-and-drop-css', 3300 3384 ), 3301 - 'uri' => '/res/pkg/d770a9ec/core.pkg.css', 3385 + 'uri' => '/res/pkg/8fd19266/core.pkg.css', 3302 3386 'type' => 'css', 3303 3387 ), 3304 3388 '66dca903' => ··· 3488 3572 'reverse' => 3489 3573 array( 3490 3574 'aphront-attached-file-view-css' => 'e30a3fa8', 3491 - 'aphront-crumbs-view-css' => 'd770a9ec', 3492 - 'aphront-dialog-view-css' => 'd770a9ec', 3493 - 'aphront-error-view-css' => 'd770a9ec', 3494 - 'aphront-form-view-css' => 'd770a9ec', 3575 + 'aphront-crumbs-view-css' => '8fd19266', 3576 + 'aphront-dialog-view-css' => '8fd19266', 3577 + 'aphront-error-view-css' => '8fd19266', 3578 + 'aphront-form-view-css' => '8fd19266', 3495 3579 'aphront-headsup-action-list-view-css' => 'ec01d039', 3496 - 'aphront-headsup-view-css' => 'd770a9ec', 3497 - 'aphront-list-filter-view-css' => 'd770a9ec', 3498 - 'aphront-pager-view-css' => 'd770a9ec', 3499 - 'aphront-panel-view-css' => 'd770a9ec', 3500 - 'aphront-table-view-css' => 'd770a9ec', 3501 - 'aphront-tokenizer-control-css' => 'd770a9ec', 3502 - 'aphront-tooltip-css' => 'd770a9ec', 3503 - 'aphront-typeahead-control-css' => 'd770a9ec', 3580 + 'aphront-headsup-view-css' => '8fd19266', 3581 + 'aphront-list-filter-view-css' => '8fd19266', 3582 + 'aphront-pager-view-css' => '8fd19266', 3583 + 'aphront-panel-view-css' => '8fd19266', 3584 + 'aphront-table-view-css' => '8fd19266', 3585 + 'aphront-tokenizer-control-css' => '8fd19266', 3586 + 'aphront-tooltip-css' => '8fd19266', 3587 + 'aphront-typeahead-control-css' => '8fd19266', 3504 3588 'differential-changeset-view-css' => 'ec01d039', 3505 3589 'differential-core-view-css' => 'ec01d039', 3506 3590 'differential-inline-comment-editor' => 'ac53d36a', ··· 3514 3598 'differential-table-of-contents-css' => 'ec01d039', 3515 3599 'diffusion-commit-view-css' => 'c8ce2d88', 3516 3600 'diffusion-icons-css' => 'c8ce2d88', 3517 - 'global-drag-and-drop-css' => 'd770a9ec', 3601 + 'global-drag-and-drop-css' => '8fd19266', 3518 3602 'inline-comment-summary-css' => 'ec01d039', 3519 3603 'javelin-aphlict' => '66dca903', 3520 3604 'javelin-behavior' => 'fbeded59', ··· 3584 3668 'javelin-util' => 'fbeded59', 3585 3669 'javelin-vector' => 'fbeded59', 3586 3670 'javelin-workflow' => 'fbeded59', 3587 - 'lightbox-attachment-css' => 'd770a9ec', 3671 + 'lightbox-attachment-css' => '8fd19266', 3588 3672 'maniphest-task-summary-css' => 'e30a3fa8', 3589 3673 'maniphest-transaction-detail-css' => 'e30a3fa8', 3590 3674 'phabricator-busy' => '66dca903', 3591 3675 'phabricator-content-source-view-css' => 'ec01d039', 3592 - 'phabricator-core-buttons-css' => 'd770a9ec', 3593 - 'phabricator-core-css' => 'd770a9ec', 3594 - 'phabricator-crumbs-view-css' => 'd770a9ec', 3595 - 'phabricator-directory-css' => 'd770a9ec', 3676 + 'phabricator-core-buttons-css' => '8fd19266', 3677 + 'phabricator-core-css' => '8fd19266', 3678 + 'phabricator-crumbs-view-css' => '8fd19266', 3679 + 'phabricator-directory-css' => '8fd19266', 3596 3680 'phabricator-drag-and-drop-file-upload' => 'ac53d36a', 3597 3681 'phabricator-dropdown-menu' => '66dca903', 3598 3682 'phabricator-file-upload' => '66dca903', 3599 - 'phabricator-filetree-view-css' => 'd770a9ec', 3600 - 'phabricator-flag-css' => 'd770a9ec', 3601 - 'phabricator-form-view-css' => 'd770a9ec', 3602 - 'phabricator-header-view-css' => 'd770a9ec', 3603 - 'phabricator-jump-nav' => 'd770a9ec', 3683 + 'phabricator-filetree-view-css' => '8fd19266', 3684 + 'phabricator-flag-css' => '8fd19266', 3685 + 'phabricator-form-view-css' => '8fd19266', 3686 + 'phabricator-header-view-css' => '8fd19266', 3687 + 'phabricator-jump-nav' => '8fd19266', 3604 3688 'phabricator-keyboard-shortcut' => '66dca903', 3605 3689 'phabricator-keyboard-shortcut-manager' => '66dca903', 3606 - 'phabricator-main-menu-view' => 'd770a9ec', 3690 + 'phabricator-main-menu-view' => '8fd19266', 3607 3691 'phabricator-menu-item' => '66dca903', 3608 - 'phabricator-nav-view-css' => 'd770a9ec', 3692 + 'phabricator-nav-view-css' => '8fd19266', 3609 3693 'phabricator-notification' => '66dca903', 3610 - 'phabricator-notification-css' => 'd770a9ec', 3611 - 'phabricator-notification-menu-css' => 'd770a9ec', 3612 - 'phabricator-object-item-list-view-css' => 'd770a9ec', 3694 + 'phabricator-notification-css' => '8fd19266', 3695 + 'phabricator-notification-menu-css' => '8fd19266', 3696 + 'phabricator-object-item-list-view-css' => '8fd19266', 3613 3697 'phabricator-object-selector-css' => 'ec01d039', 3614 3698 'phabricator-paste-file-upload' => '66dca903', 3615 3699 'phabricator-prefab' => '66dca903', 3616 3700 'phabricator-project-tag-css' => 'e30a3fa8', 3617 - 'phabricator-remarkup-css' => 'd770a9ec', 3701 + 'phabricator-remarkup-css' => '8fd19266', 3618 3702 'phabricator-shaped-request' => 'ac53d36a', 3619 - 'phabricator-side-menu-view-css' => 'd770a9ec', 3620 - 'phabricator-standard-page-view' => 'd770a9ec', 3703 + 'phabricator-side-menu-view-css' => '8fd19266', 3704 + 'phabricator-standard-page-view' => '8fd19266', 3621 3705 'phabricator-textareautils' => '66dca903', 3622 3706 'phabricator-tooltip' => '66dca903', 3623 - 'phabricator-transaction-view-css' => 'd770a9ec', 3624 - 'phabricator-zindex-css' => 'd770a9ec', 3625 - 'sprite-apps-large-css' => 'd770a9ec', 3626 - 'sprite-gradient-css' => 'd770a9ec', 3627 - 'sprite-icon-css' => 'd770a9ec', 3628 - 'sprite-menu-css' => 'd770a9ec', 3629 - 'syntax-highlighting-css' => 'd770a9ec', 3707 + 'phabricator-transaction-view-css' => '8fd19266', 3708 + 'phabricator-zindex-css' => '8fd19266', 3709 + 'sprite-apps-large-css' => '8fd19266', 3710 + 'sprite-gradient-css' => '8fd19266', 3711 + 'sprite-icon-css' => '8fd19266', 3712 + 'sprite-menu-css' => '8fd19266', 3713 + 'syntax-highlighting-css' => '8fd19266', 3630 3714 ), 3631 3715 ));
+43
src/__phutil_library_map__.php
··· 195 195 'ConduitCallTestCase' => 'applications/conduit/call/__tests__/ConduitCallTestCase.php', 196 196 'ConduitException' => 'applications/conduit/protocol/ConduitException.php', 197 197 'ConduitSSHWorkflow' => 'applications/conduit/ssh/ConduitSSHWorkflow.php', 198 + 'ConpherenceConstants' => 'applications/conpherence/constants/ConpherenceConstants.php', 199 + 'ConpherenceController' => 'applications/conpherence/controller/ConpherenceController.php', 200 + 'ConpherenceDAO' => 'applications/conpherence/storage/ConpherenceDAO.php', 201 + 'ConpherenceEditor' => 'applications/conpherence/editor/ConpherenceEditor.php', 202 + 'ConpherenceListController' => 'applications/conpherence/controller/ConpherenceListController.php', 203 + 'ConpherenceMenuItemView' => 'applications/conpherence/view/ConpherenceMenuItemView.php', 204 + 'ConpherenceNewController' => 'applications/conpherence/controller/ConpherenceNewController.php', 205 + 'ConpherenceParticipant' => 'applications/conpherence/storage/ConpherenceParticipant.php', 206 + 'ConpherenceParticipantQuery' => 'applications/conpherence/query/ConpherenceParticipantQuery.php', 207 + 'ConpherenceParticipationStatus' => 'applications/conpherence/constants/ConpherenceParticipationStatus.php', 208 + 'ConpherenceThread' => 'applications/conpherence/storage/ConpherenceThread.php', 209 + 'ConpherenceThreadQuery' => 'applications/conpherence/query/ConpherenceThreadQuery.php', 210 + 'ConpherenceTransaction' => 'applications/conpherence/storage/ConpherenceTransaction.php', 211 + 'ConpherenceTransactionComment' => 'applications/conpherence/storage/ConpherenceTransactionComment.php', 212 + 'ConpherenceTransactionQuery' => 'applications/conpherence/query/ConpherenceTransactionQuery.php', 213 + 'ConpherenceTransactionType' => 'applications/conpherence/constants/ConpherenceTransactionType.php', 214 + 'ConpherenceTransactionView' => 'applications/conpherence/view/ConpherenceTransactionView.php', 215 + 'ConpherenceUpdateController' => 'applications/conpherence/controller/ConpherenceUpdateController.php', 216 + 'ConpherenceViewController' => 'applications/conpherence/controller/ConpherenceViewController.php', 198 217 'DarkConsoleController' => 'aphront/console/DarkConsoleController.php', 199 218 'DarkConsoleCore' => 'aphront/console/DarkConsoleCore.php', 200 219 'DarkConsoleErrorLogPlugin' => 'aphront/console/plugin/DarkConsoleErrorLogPlugin.php', ··· 602 621 'PhabricatorApplicationConduit' => 'applications/conduit/application/PhabricatorApplicationConduit.php', 603 622 'PhabricatorApplicationConfig' => 'applications/config/application/PhabricatorApplicationConfig.php', 604 623 'PhabricatorApplicationConfigOptions' => 'applications/config/option/PhabricatorApplicationConfigOptions.php', 624 + 'PhabricatorApplicationConpherence' => 'applications/conpherence/application/PhabricatorApplicationConpherence.php', 605 625 'PhabricatorApplicationCountdown' => 'applications/countdown/application/PhabricatorApplicationCountdown.php', 606 626 'PhabricatorApplicationDaemons' => 'applications/daemon/application/PhabricatorApplicationDaemons.php', 607 627 'PhabricatorApplicationDetailViewController' => 'applications/meta/controller/PhabricatorApplicationDetailViewController.php', ··· 1646 1666 'ConduitCallTestCase' => 'PhabricatorTestCase', 1647 1667 'ConduitException' => 'Exception', 1648 1668 'ConduitSSHWorkflow' => 'PhabricatorSSHWorkflow', 1669 + 'ConpherenceController' => 'PhabricatorController', 1670 + 'ConpherenceDAO' => 'PhabricatorLiskDAO', 1671 + 'ConpherenceEditor' => 'PhabricatorApplicationTransactionEditor', 1672 + 'ConpherenceListController' => 'ConpherenceController', 1673 + 'ConpherenceMenuItemView' => 'AphrontTagView', 1674 + 'ConpherenceNewController' => 'ConpherenceController', 1675 + 'ConpherenceParticipant' => 'ConpherenceDAO', 1676 + 'ConpherenceParticipantQuery' => 'PhabricatorOffsetPagedQuery', 1677 + 'ConpherenceParticipationStatus' => 'ConpherenceConstants', 1678 + 'ConpherenceThread' => 1679 + array( 1680 + 0 => 'ConpherenceDAO', 1681 + 1 => 'PhabricatorPolicyInterface', 1682 + ), 1683 + 'ConpherenceThreadQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 1684 + 'ConpherenceTransaction' => 'PhabricatorApplicationTransaction', 1685 + 'ConpherenceTransactionComment' => 'PhabricatorApplicationTransactionComment', 1686 + 'ConpherenceTransactionQuery' => 'PhabricatorApplicationTransactionQuery', 1687 + 'ConpherenceTransactionType' => 'ConpherenceConstants', 1688 + 'ConpherenceTransactionView' => 'AphrontView', 1689 + 'ConpherenceUpdateController' => 'ConpherenceController', 1690 + 'ConpherenceViewController' => 'ConpherenceController', 1649 1691 'DarkConsoleController' => 'PhabricatorController', 1650 1692 'DarkConsoleErrorLogPlugin' => 'DarkConsolePlugin', 1651 1693 'DarkConsoleEventPlugin' => 'DarkConsolePlugin', ··· 1998 2040 'PhabricatorApplicationConduit' => 'PhabricatorApplication', 1999 2041 'PhabricatorApplicationConfig' => 'PhabricatorApplication', 2000 2042 'PhabricatorApplicationConfigOptions' => 'Phobject', 2043 + 'PhabricatorApplicationConpherence' => 'PhabricatorApplication', 2001 2044 'PhabricatorApplicationCountdown' => 'PhabricatorApplication', 2002 2045 'PhabricatorApplicationDaemons' => 'PhabricatorApplication', 2003 2046 'PhabricatorApplicationDetailViewController' => 'PhabricatorApplicationsController',
+48
src/applications/conpherence/application/PhabricatorApplicationConpherence.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class PhabricatorApplicationConpherence extends PhabricatorApplication { 7 + 8 + public function isBeta() { 9 + return true; 10 + } 11 + 12 + public function getBaseURI() { 13 + return '/conpherence/'; 14 + } 15 + 16 + public function getQuickCreateURI() { 17 + return $this->getBaseURI().'new/'; 18 + } 19 + 20 + public function getShortDescription() { 21 + return pht('Messaging'); 22 + } 23 + 24 + public function getIconName() { 25 + return 'conpherence'; 26 + } 27 + 28 + public function getTitleGlyph() { 29 + return "\xE2\x98\x8E"; 30 + } 31 + 32 + public function getApplicationGroup() { 33 + return self::GROUP_COMMUNICATION; 34 + } 35 + 36 + public function getRoutes() { 37 + return array( 38 + '/conpherence/' => array( 39 + '' => 'ConpherenceListController', 40 + 'new/' => 'ConpherenceNewController', 41 + 'view/(?P<id>[1-9]\d*)/' => 'ConpherenceViewController', 42 + 'update/(?P<id>[1-9]\d*)/' => 'ConpherenceUpdateController', 43 + '(?P<id>[1-9]\d*)/' => 'ConpherenceListController', 44 + ), 45 + ); 46 + } 47 + 48 + }
+8
src/applications/conpherence/constants/ConpherenceConstants.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + abstract class ConpherenceConstants { 7 + 8 + }
+8
src/applications/conpherence/constants/ConpherenceParticipationStatus.php
··· 1 + <?php 2 + 3 + final class ConpherenceParticipationStatus extends ConpherenceConstants { 4 + 5 + const UP_TO_DATE = 0; 6 + const BEHIND = 1; 7 + 8 + }
+13
src/applications/conpherence/constants/ConpherenceTransactionType.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceTransactionType extends ConpherenceConstants { 7 + 8 + const TYPE_FILES = 'files'; 9 + const TYPE_TITLE = 'title'; 10 + const TYPE_PICTURE = 'picture'; 11 + const TYPE_PARTICIPANTS = 'participants'; 12 + 13 + }
+225
src/applications/conpherence/controller/ConpherenceController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + abstract class ConpherenceController extends PhabricatorController { 7 + private $conpherences; 8 + private $selectedConpherencePHID; 9 + private $readConpherences; 10 + private $unreadConpherences; 11 + 12 + public function setUnreadConpherences(array $conpherences) { 13 + assert_instances_of($conpherences, 'ConpherenceThread'); 14 + $this->unreadConpherences = $conpherences; 15 + return $this; 16 + } 17 + public function getUnreadConpherences() { 18 + return $this->unreadConpherences; 19 + } 20 + 21 + public function setReadConpherences(array $conpherences) { 22 + assert_instances_of($conpherences, 'ConpherenceThread'); 23 + $this->readConpherences = $conpherences; 24 + return $this; 25 + } 26 + public function getReadConpherences() { 27 + return $this->readConpherences; 28 + } 29 + 30 + public function setSelectedConpherencePHID($phid) { 31 + $this->selectedConpherencePHID = $phid; 32 + return $this; 33 + } 34 + public function getSelectedConpherencePHID() { 35 + return $this->selectedConpherencePHID; 36 + } 37 + 38 + /** 39 + * Try for a full set of unread conpherences, and if we fail 40 + * load read conpherences. Additional conpherences in either category 41 + * are loaded asynchronously. 42 + */ 43 + public function loadStartingConpherences($current_selection_epoch = null) { 44 + $user = $this->getRequest()->getUser(); 45 + 46 + $read_participant_query = id(new ConpherenceParticipantQuery()) 47 + ->withParticipantPHIDs(array($user->getPHID())); 48 + $read_status = ConpherenceParticipationStatus::UP_TO_DATE; 49 + if ($current_selection_epoch) { 50 + $read_one = $read_participant_query 51 + ->withParticipationStatus($read_status) 52 + ->withDateTouched($current_selection_epoch, '>') 53 + ->execute(); 54 + 55 + $read_two = $read_participant_query 56 + ->withDateTouched($current_selection_epoch, '<=') 57 + ->execute(); 58 + 59 + $read = array_merge($read_one, $read_two); 60 + 61 + } else { 62 + $read = $read_participant_query 63 + ->withParticipationStatus($read_status) 64 + ->execute(); 65 + } 66 + 67 + $unread_status = ConpherenceParticipationStatus::BEHIND; 68 + $unread_participant_query = id(new ConpherenceParticipantQuery()) 69 + ->withParticipantPHIDs(array($user->getPHID())); 70 + $unread = $unread_participant_query 71 + ->withParticipationStatus($unread_status) 72 + ->execute(); 73 + 74 + $all_participation = $unread + $read; 75 + $all_conpherence_phids = array_keys($all_participation); 76 + $all_conpherences = id(new ConpherenceThreadQuery()) 77 + ->setViewer($user) 78 + ->withPHIDs($all_conpherence_phids) 79 + ->execute(); 80 + $unread_conpherences = array_select_keys( 81 + $all_conpherences, 82 + array_keys($unread) 83 + ); 84 + $this->setUnreadConpherences($unread_conpherences); 85 + 86 + $read_conpherences = array_select_keys( 87 + $all_conpherences, 88 + array_keys($read) 89 + ); 90 + $this->setReadConpherences($read_conpherences); 91 + 92 + if (!$this->getSelectedConpherencePHID()) { 93 + $this->setSelectedConpherencePHID(reset($all_conpherence_phids)); 94 + } 95 + 96 + return $this; 97 + } 98 + 99 + public function buildSideNavView($filter = null) { 100 + require_celerity_resource('conpherence-menu-css'); 101 + $unread_conpherences = $this->getUnreadConpherences(); 102 + $read_conpherences = $this->getReadConpherences(); 103 + 104 + $user = $this->getRequest()->getUser(); 105 + 106 + $menu = new PhabricatorMenuView(); 107 + $nav = AphrontSideNavFilterView::newFromMenu($menu); 108 + $nav->addClass('conpherence-menu'); 109 + $nav->setMenuID('conpherence-menu'); 110 + 111 + $nav->addFilter( 112 + 'new', 113 + pht('New Conpherence'), 114 + $this->getApplicationURI('new/') 115 + ); 116 + $nav->addLabel(pht('Unread')); 117 + $nav = $this->addConpherencesToNav($unread_conpherences, $nav); 118 + 119 + $nav->addLabel(pht('Read')); 120 + $nav = $this->addConpherencesToNav($read_conpherences, $nav, true); 121 + 122 + $nav->selectFilter($filter); 123 + 124 + return $nav; 125 + } 126 + 127 + private function addConpherencesToNav( 128 + array $conpherences, 129 + AphrontSideNavFilterView $nav, 130 + $read = false) { 131 + 132 + $user = $this->getRequest()->getUser(); 133 + foreach ($conpherences as $conpherence) { 134 + $uri = $this->getApplicationURI('view/'.$conpherence->getID().'/'); 135 + $data = $conpherence->getDisplayData($user); 136 + $title = $data['title']; 137 + $subtitle = $data['subtitle']; 138 + $unread_count = $data['unread_count']; 139 + $epoch = $data['epoch']; 140 + $image = $data['image']; 141 + $snippet = $data['snippet']; 142 + 143 + $item = id(new ConpherenceMenuItemView()) 144 + ->setUser($user) 145 + ->setTitle($title) 146 + ->setSubtitle($subtitle) 147 + ->setHref($uri) 148 + ->setEpoch($epoch) 149 + ->setImageURI($image) 150 + ->setMessageText($snippet) 151 + ->setUnreadCount($unread_count) 152 + ->setID($conpherence->getPHID()) 153 + ->addSigil('conpherence-menu-click') 154 + ->setMetadata(array('id' => $conpherence->getID())); 155 + if ($this->getSelectedConpherencePHID() == $conpherence->getPHID()) { 156 + $item->addClass('conpherence-selected'); 157 + $item->addClass('hide-unread-count'); 158 + } 159 + $nav->addCustomBlock($item->render()); 160 + } 161 + if (empty($conpherences) || $read) { 162 + $nav->addCustomBlock($this->getNoConpherencesBlock()); 163 + } 164 + 165 + return $nav; 166 + } 167 + 168 + private function getNoConpherencesBlock() { 169 + 170 + return phutil_render_tag( 171 + 'div', 172 + array( 173 + 'class' => 'no-conpherences-menu-item' 174 + ), 175 + pht('No more conpherences.') 176 + ); 177 + } 178 + 179 + public function buildApplicationMenu() { 180 + return $this->buildSideNavView()->getMenu(); 181 + } 182 + 183 + public function buildApplicationCrumbs() { 184 + $crumbs = parent::buildApplicationCrumbs(); 185 + 186 + $crumbs 187 + ->addAction( 188 + id(new PhabricatorMenuItemView()) 189 + ->setName(pht('New Conpherence')) 190 + ->setHref($this->getApplicationURI('new/')) 191 + ->setIcon('create') 192 + ) 193 + ->addCrumb( 194 + id(new PhabricatorCrumbView()) 195 + ->setName(pht('Conpherence')) 196 + ); 197 + 198 + return $crumbs; 199 + } 200 + 201 + protected function initJavelinBehaviors() { 202 + 203 + Javelin::initBehavior('conpherence-menu', 204 + array( 205 + 'base_uri' => $this->getApplicationURI(''), 206 + 'header' => 'conpherence-header-pane', 207 + 'messages' => 'conpherence-messages', 208 + 'widgets_pane' => 'conpherence-widget-pane', 209 + 'form_pane' => 'conpherence-form', 210 + 'fancy_ajax' => (bool) $this->getSelectedConpherencePHID() 211 + ) 212 + ); 213 + Javelin::initBehavior('conpherence-init', 214 + array( 215 + 'selected_conpherence_id' => $this->getSelectedConpherencePHID(), 216 + 'menu_pane' => 'conpherence-menu', 217 + 'messages_pane' => 'conpherence-message-pane', 218 + 'messages' => 'conpherence-messages', 219 + 'widgets_pane' => 'conpherence-widget-pane', 220 + 'form_pane' => 'conpherence-form' 221 + ) 222 + ); 223 + } 224 + 225 + }
+119
src/applications/conpherence/controller/ConpherenceListController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceListController extends 7 + ConpherenceController { 8 + 9 + private $conpherenceID; 10 + 11 + public function setConpherenceID($conpherence_id) { 12 + $this->conpherenceID = $conpherence_id; 13 + return $this; 14 + } 15 + public function getConpherenceID() { 16 + return $this->conpherenceID; 17 + } 18 + 19 + public function willProcessRequest(array $data) { 20 + $this->setConpherenceID(idx($data, 'id')); 21 + } 22 + 23 + public function processRequest() { 24 + $request = $this->getRequest(); 25 + $user = $request->getUser(); 26 + $title = pht('Conpherence'); 27 + 28 + $conpherence_id = $this->getConpherenceID(); 29 + $current_selection_epoch = null; 30 + if ($conpherence_id) { 31 + $conpherence = id(new ConpherenceThreadQuery()) 32 + ->setViewer($user) 33 + ->withIDs(array($conpherence_id)) 34 + ->executeOne(); 35 + if (!$conpherence) { 36 + return new Aphront404Response(); 37 + } 38 + 39 + if ($conpherence->getTitle()) { 40 + $title = $conpherence->getTitle(); 41 + } 42 + $this->setSelectedConpherencePHID($conpherence->getPHID()); 43 + 44 + $read_status = ConpherenceParticipationStatus::UP_TO_DATE; 45 + $participant = $conpherence->getParticipant($user->getPHID()); 46 + $write_guard = AphrontWriteGuard::beginScopedUnguardedWrites(); 47 + $participant->markUpToDate(); 48 + unset($write_guard); 49 + $current_selection_epoch = $participant->getDateTouched(); 50 + } 51 + 52 + $this->loadStartingConpherences($current_selection_epoch); 53 + $nav = $this->buildSideNavView(); 54 + 55 + $main_pane = $this->renderEmptyMainPane(); 56 + $nav->appendChild( 57 + array( 58 + $main_pane, 59 + )); 60 + 61 + return $this->buildApplicationPage( 62 + $nav, 63 + array( 64 + 'title' => $title, 65 + 'device' => true, 66 + ) 67 + ); 68 + } 69 + 70 + private function renderEmptyMainPane() { 71 + $this->initJavelinBehaviors(); 72 + return phutil_render_tag( 73 + 'div', 74 + array( 75 + 'id' => 'conpherence-main-pane' 76 + ), 77 + phutil_render_tag( 78 + 'div', 79 + array( 80 + 'class' => 'conpherence-header-pane', 81 + 'id' => 'conpherence-header-pane', 82 + ), 83 + '' 84 + ). 85 + phutil_render_tag( 86 + 'div', 87 + array( 88 + 'class' => 'conpherence-widget-pane', 89 + 'id' => 'conpherence-widget-pane' 90 + ), 91 + '' 92 + ). 93 + javelin_render_tag( 94 + 'div', 95 + array( 96 + 'class' => 'conpherence-message-pane', 97 + 'id' => 'conpherence-message-pane' 98 + ), 99 + phutil_render_tag( 100 + 'div', 101 + array( 102 + 'class' => 'conpherence-messages', 103 + 'id' => 'conpherence-messages' 104 + ), 105 + '' 106 + ). 107 + phutil_render_tag( 108 + 'div', 109 + array( 110 + 'id' => 'conpherence-form' 111 + ), 112 + '' 113 + ) 114 + ) 115 + ); 116 + } 117 + 118 + 119 + }
+201
src/applications/conpherence/controller/ConpherenceNewController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceNewController extends ConpherenceController { 7 + 8 + public function processRequest() { 9 + $request = $this->getRequest(); 10 + $user = $request->getUser(); 11 + 12 + $conpherence = id(new ConpherenceThread()) 13 + ->attachParticipants(array()) 14 + ->attachFilePHIDs(array()); 15 + $title = pht('New Conpherence'); 16 + $participants = array(); 17 + $message = ''; 18 + $files = array(); 19 + $errors = array(); 20 + $e_participants = null; 21 + $e_message = null; 22 + 23 + // this comes from ajax requests from all over. should be a single phid. 24 + $participant_prefill = $request->getStr('participant'); 25 + if ($participant_prefill) { 26 + $participants[] = $participant_prefill; 27 + } 28 + 29 + if ($request->isFormPost()) { 30 + $participants = $request->getArr('participants'); 31 + if (empty($participants)) { 32 + $e_participants = true; 33 + $errors[] = pht('You must specify participants.'); 34 + } else { 35 + $participants[] = $user->getPHID(); 36 + $participants = array_unique($participants); 37 + } 38 + 39 + $message = $request->getStr('message'); 40 + if (empty($message)) { 41 + $e_message = true; 42 + $errors[] = pht('You must write a message.'); 43 + } 44 + 45 + $file_phids = 46 + PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( 47 + array($message) 48 + ); 49 + if ($file_phids) { 50 + $files = id(new PhabricatorFileQuery()) 51 + ->setViewer($user) 52 + ->withPHIDs($file_phids) 53 + ->execute(); 54 + } 55 + 56 + if (!$errors) { 57 + $conpherence->openTransaction(); 58 + $conpherence->save(); 59 + $xactions = array(); 60 + $xactions[] = id(new ConpherenceTransaction()) 61 + ->setTransactionType(ConpherenceTransactionType::TYPE_PARTICIPANTS) 62 + ->setNewValue(array('+' => $participants)); 63 + $xactions[] = id(new ConpherenceTransaction()) 64 + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 65 + ->attachComment( 66 + id(new ConpherenceTransactionComment()) 67 + ->setContent($message) 68 + ->setConpherencePHID($conpherence->getPHID()) 69 + ); 70 + if ($files) { 71 + $xactions[] = id(new ConpherenceTransaction()) 72 + ->setTransactionType(ConpherenceTransactionType::TYPE_FILES) 73 + ->setNewValue(array('+' => mpull($files, 'getPHID'))); 74 + } 75 + $content_source = PhabricatorContentSource::newForSource( 76 + PhabricatorContentSource::SOURCE_WEB, 77 + array( 78 + 'ip' => $request->getRemoteAddr() 79 + ) 80 + ); 81 + id(new ConpherenceEditor()) 82 + ->setContentSource($content_source) 83 + ->setContinueOnNoEffect(true) 84 + ->setActor($user) 85 + ->applyTransactions($conpherence, $xactions); 86 + 87 + $conpherence->saveTransaction(); 88 + 89 + if ($request->isAjax()) { 90 + $dialog = id(new AphrontDialogView()) 91 + ->setUser($user) 92 + ->setTitle('Success') 93 + ->addCancelButton('#', 'Okay') 94 + ->appendChild( 95 + phutil_render_tag('p', 96 + array(), 97 + pht('Message sent successfully.') 98 + ) 99 + ); 100 + $response = id(new AphrontDialogResponse()) 101 + ->setDialog($dialog); 102 + } else { 103 + $uri = $this->getApplicationURI($conpherence->getID()); 104 + $response = id(new AphrontRedirectResponse()) 105 + ->setURI($uri); 106 + } 107 + return $response; 108 + } 109 + } 110 + 111 + $error_view = null; 112 + if ($errors) { 113 + $error_view = id(new AphrontErrorView()) 114 + ->setTitle(pht('Conpherence Errors')) 115 + ->setErrors($errors); 116 + } 117 + 118 + $participant_handles = array(); 119 + if ($participants) { 120 + $handles = id(new PhabricatorObjectHandleData($participants)) 121 + ->setViewer($user) 122 + ->loadHandles(); 123 + $participant_handles = mpull($handles, 'getFullName', 'getPHID'); 124 + } 125 + 126 + $submit_uri = $this->getApplicationURI('new/'); 127 + $cancel_uri = $this->getApplicationURI(); 128 + if ($request->isAjax()) { 129 + // TODO - we can get a better cancel_uri once we get better at crazy 130 + // ajax jonx T2086 131 + if ($participant_prefill) { 132 + $handle = $handles[$participant_prefill]; 133 + $cancel_uri = $handle->getURI(); 134 + } 135 + $form = id(new AphrontDialogView()) 136 + ->setSubmitURI($submit_uri) 137 + ->addSubmitButton() 138 + ->setWidth(AphrontDialogView::WIDTH_FORM) 139 + ->addCancelButton($cancel_uri); 140 + } else { 141 + $form = id(new AphrontFormView()) 142 + ->setID('conpherence-message-pane') 143 + ->setAction($submit_uri); 144 + } 145 + 146 + $form 147 + ->setUser($user) 148 + ->appendChild( 149 + id(new AphrontFormTokenizerControl()) 150 + ->setName('participants') 151 + ->setValue($participant_handles) 152 + ->setUser($user) 153 + ->setDatasource('/typeahead/common/users/') 154 + ->setLabel(pht('To')) 155 + ->setError($e_participants) 156 + ) 157 + ->appendChild( 158 + id(new PhabricatorRemarkupControl()) 159 + ->setName('message') 160 + ->setValue($message) 161 + ->setLabel(pht('Message')) 162 + ->setPlaceHolder(pht('Drag and drop to include files...')) 163 + ->setError($e_message) 164 + ); 165 + 166 + if ($request->isAjax()) { 167 + $form->setTitle($title); 168 + return id(new AphrontDialogResponse()) 169 + ->setDialog($form); 170 + } 171 + 172 + $form 173 + ->appendChild( 174 + id(new AphrontFormSubmitControl()) 175 + ->setValue(pht('Submit')) 176 + ->addCancelButton($cancel_uri) 177 + ); 178 + 179 + $this->loadStartingConpherences(); 180 + $this->setSelectedConpherencePHID(null); 181 + $this->initJavelinBehaviors(); 182 + $nav = $this->buildSideNavView('new'); 183 + $header = id(new PhabricatorHeaderView()) 184 + ->setHeader($title); 185 + 186 + $nav->appendChild( 187 + array( 188 + $header, 189 + $error_view, 190 + $form, 191 + )); 192 + 193 + return $this->buildApplicationPage( 194 + $nav, 195 + array( 196 + 'title' => $title, 197 + 'device' => true, 198 + ) 199 + ); 200 + } 201 + }
+219
src/applications/conpherence/controller/ConpherenceUpdateController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceUpdateController extends 7 + ConpherenceController { 8 + 9 + private $conpherenceID; 10 + 11 + public function setConpherenceID($conpherence_id) { 12 + $this->conpherenceID = $conpherence_id; 13 + return $this; 14 + } 15 + public function getConpherenceID() { 16 + return $this->conpherenceID; 17 + } 18 + public function willProcessRequest(array $data) { 19 + $this->setConpherenceID(idx($data, 'id')); 20 + } 21 + 22 + public function processRequest() { 23 + $request = $this->getRequest(); 24 + $user = $request->getUser(); 25 + $conpherence_id = $this->getConpherenceID(); 26 + if (!$conpherence_id) { 27 + return new Aphront404Response(); 28 + } 29 + 30 + $conpherence = id(new ConpherenceThreadQuery()) 31 + ->setViewer($user) 32 + ->withIDs(array($conpherence_id)) 33 + ->executeOne(); 34 + $supported_formats = PhabricatorFile::getTransformableImageFormats(); 35 + 36 + $updated = false; 37 + $error_view = null; 38 + $e_image = null; 39 + $errors = array(); 40 + if ($request->isFormPost()) { 41 + $content_source = PhabricatorContentSource::newForSource( 42 + PhabricatorContentSource::SOURCE_WEB, 43 + array( 44 + 'ip' => $request->getRemoteAddr() 45 + )); 46 + 47 + $action = $request->getStr('action'); 48 + switch ($action) { 49 + case 'message': 50 + $message = $request->getStr('text'); 51 + $files = array(); 52 + $file_phids = 53 + PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( 54 + array($message) 55 + ); 56 + if ($file_phids) { 57 + $files = id(new PhabricatorFileQuery()) 58 + ->setViewer($user) 59 + ->withPHIDs($file_phids) 60 + ->execute(); 61 + } 62 + $xactions = array(); 63 + if ($files) { 64 + $xactions[] = id(new ConpherenceTransaction()) 65 + ->setTransactionType(ConpherenceTransactionType::TYPE_FILES) 66 + ->setNewValue(array('+' => mpull($files, 'getPHID'))); 67 + } 68 + $xactions[] = id(new ConpherenceTransaction()) 69 + ->setTransactionType(PhabricatorTransactions::TYPE_COMMENT) 70 + ->attachComment( 71 + id(new ConpherenceTransactionComment()) 72 + ->setContent($message) 73 + ->setConpherencePHID($conpherence->getPHID()) 74 + ); 75 + $time = time(); 76 + $conpherence->openTransaction(); 77 + $xactions = id(new ConpherenceEditor()) 78 + ->setContentSource($content_source) 79 + ->setActor($user) 80 + ->applyTransactions($conpherence, $xactions); 81 + $last_xaction = end($xactions); 82 + $xaction_phid = $last_xaction->getPHID(); 83 + $behind = ConpherenceParticipationStatus::BEHIND; 84 + $up_to_date = ConpherenceParticipationStatus::UP_TO_DATE; 85 + $participants = $conpherence->getParticipants(); 86 + foreach ($participants as $phid => $participant) { 87 + if ($phid != $user->getPHID()) { 88 + if ($participant->getParticipationStatus() != $behind) { 89 + $participant->setBehindTransactionPHID($xaction_phid); 90 + } 91 + $participant->setParticipationStatus($behind); 92 + $participant->setDateTouched($time); 93 + } else { 94 + $participant->setParticipationStatus($up_to_date); 95 + $participant->setDateTouched($time); 96 + } 97 + $participant->save(); 98 + } 99 + $updated = $conpherence->saveTransaction(); 100 + break; 101 + case 'metadata': 102 + $xactions = array(); 103 + $default_image = $request->getExists('default_image'); 104 + if ($default_image) { 105 + $image_phid = null; 106 + $xactions[] = id(new ConpherenceTransaction()) 107 + ->setTransactionType(ConpherenceTransactionType::TYPE_PICTURE) 108 + ->setNewValue($image_phid); 109 + } else if (!empty($_FILES['image'])) { 110 + $err = idx($_FILES['image'], 'error'); 111 + if ($err != UPLOAD_ERR_NO_FILE) { 112 + $file = PhabricatorFile::newFromPHPUpload( 113 + $_FILES['image'], 114 + array( 115 + 'authorPHID' => $user->getPHID(), 116 + )); 117 + $okay = $file->isTransformableImage(); 118 + if ($okay) { 119 + $xformer = new PhabricatorImageTransformer(); 120 + $xformed = $xformer->executeThumbTransform( 121 + $file, 122 + $x = 50, 123 + $y = 50); 124 + $image_phid = $xformed->getPHID(); 125 + $xactions[] = id(new ConpherenceTransaction()) 126 + ->setTransactionType(ConpherenceTransactionType::TYPE_PICTURE) 127 + ->setNewValue($image_phid); 128 + } else { 129 + $e_image = pht('Not Supported'); 130 + $errors[] = 131 + pht('This server only supports these image formats: %s.', 132 + implode(', ', $supported_formats)); 133 + } 134 + } 135 + } 136 + $title = $request->getStr('title'); 137 + if ($title != $conpherence->getTitle()) { 138 + $xactions[] = id(new ConpherenceTransaction()) 139 + ->setTransactionType(ConpherenceTransactionType::TYPE_TITLE) 140 + ->setNewValue($title); 141 + } 142 + 143 + if ($xactions) { 144 + $conpherence->openTransaction(); 145 + $xactions = id(new ConpherenceEditor()) 146 + ->setContentSource($content_source) 147 + ->setActor($user) 148 + ->setContinueOnNoEffect(true) 149 + ->applyTransactions($conpherence, $xactions); 150 + $updated = $conpherence->saveTransaction(); 151 + } else if (empty($errors)) { 152 + $errors[] = pht( 153 + 'That was a non-update. Try cancel.' 154 + ); 155 + } 156 + break; 157 + default: 158 + throw new Exception('Unknown action: '.$action); 159 + break; 160 + } 161 + } 162 + 163 + if ($updated) { 164 + return id(new AphrontRedirectResponse())->setURI( 165 + $this->getApplicationURI($conpherence_id.'/') 166 + ); 167 + } 168 + 169 + if ($errors) { 170 + $error_view = id(new AphrontErrorView()) 171 + ->setTitle(pht('Errors editing conpherence.')) 172 + ->setErrors($errors); 173 + } 174 + 175 + $form = id(new AphrontFormLayoutView()) 176 + ->appendChild( 177 + id(new AphrontFormTextControl()) 178 + ->setLabel(pht('Title')) 179 + ->setName('title') 180 + ->setValue($conpherence->getTitle()) 181 + ) 182 + ->appendChild( 183 + id(new AphrontFormMarkupControl()) 184 + ->setLabel(pht('Image')) 185 + ->setValue(phutil_render_tag( 186 + 'img', 187 + array( 188 + 'src' => $conpherence->loadImageURI(), 189 + )) 190 + ) 191 + ) 192 + ->appendChild( 193 + id(new AphrontFormImageControl()) 194 + ->setLabel(pht('Change Image')) 195 + ->setName('image') 196 + ->setCaption('Supported formats: '.implode(', ', $supported_formats)) 197 + ->setError($e_image) 198 + ); 199 + 200 + // TODO -- fix javelin so we can upload files from a workflow 201 + require_celerity_resource('conpherence-update-css'); 202 + return $this->buildStandardPageResponse( 203 + array( 204 + $error_view, 205 + id(new AphrontDialogView()) 206 + ->setUser($user) 207 + ->setTitle(pht('Update Conpherence')) 208 + ->setWidth(AphrontDialogView::WIDTH_FORM) 209 + ->setSubmitURI($this->getApplicationURI('update/'.$conpherence_id.'/')) 210 + ->setEncType('multipart/form-data') 211 + ->addHiddenInput('action', 'metadata') 212 + ->appendChild($form) 213 + ->addSubmitButton() 214 + ->addCancelButton($this->getApplicationURI($conpherence->getID().'/')), 215 + ), 216 + array() 217 + ); 218 + } 219 + }
+291
src/applications/conpherence/controller/ConpherenceViewController.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceViewController extends 7 + ConpherenceController { 8 + 9 + private $conpherenceID; 10 + private $conpherence; 11 + 12 + public function setConpherence(ConpherenceThread $conpherence) { 13 + $this->conpherence = $conpherence; 14 + return $this; 15 + } 16 + public function getConpherence() { 17 + return $this->conpherence; 18 + } 19 + 20 + public function setConpherenceID($conpherence_id) { 21 + $this->conpherenceID = $conpherence_id; 22 + return $this; 23 + } 24 + public function getConpherenceID() { 25 + return $this->conpherenceID; 26 + } 27 + 28 + public function willProcessRequest(array $data) { 29 + $this->setConpherenceID(idx($data, 'id')); 30 + } 31 + 32 + public function processRequest() { 33 + $request = $this->getRequest(); 34 + $user = $request->getUser(); 35 + 36 + $conpherence_id = $this->getConpherenceID(); 37 + if (!$conpherence_id) { 38 + return new Aphront404Response(); 39 + } 40 + if (!$request->isAjax()) { 41 + return id(new AphrontRedirectResponse()) 42 + ->setURI($this->getApplicationURI($conpherence_id.'/')); 43 + } 44 + $conpherence = id(new ConpherenceThreadQuery()) 45 + ->setViewer($user) 46 + ->withIDs(array($conpherence_id)) 47 + ->needWidgetData(true) 48 + ->executeOne(); 49 + $this->setConpherence($conpherence); 50 + 51 + $participant = $conpherence->getParticipant($user->getPHID()); 52 + $write_guard = AphrontWriteGuard::beginScopedUnguardedWrites(); 53 + $participant->markUpToDate(); 54 + unset($write_guard); 55 + 56 + $header = $this->renderHeaderPaneContent(); 57 + $messages = $this->renderMessagePaneContent(); 58 + $widgets = $this->renderWidgetPaneContent(); 59 + $content = $header + $widgets + $messages; 60 + 61 + return id(new AphrontAjaxResponse())->setContent($content); 62 + } 63 + 64 + private function renderHeaderPaneContent() { 65 + require_celerity_resource('conpherence-header-pane-css'); 66 + $user = $this->getRequest()->getUser(); 67 + $conpherence = $this->getConpherence(); 68 + $display_data = $conpherence->getDisplayData($user); 69 + $edit_href = $this->getApplicationURI('update/'.$conpherence->getID().'/'); 70 + 71 + $header = 72 + phutil_render_tag( 73 + 'a', 74 + array( 75 + 'class' => 'edit', 76 + 'href' => $edit_href, 77 + ), 78 + pht('edit...') 79 + ). 80 + phutil_render_tag( 81 + 'div', 82 + array( 83 + 'class' => 'header-image', 84 + 'style' => 'background-image: url('.$display_data['image'].');' 85 + ), 86 + '' 87 + ). 88 + phutil_render_tag( 89 + 'div', 90 + array( 91 + 'class' => 'title', 92 + ), 93 + phutil_escape_html($display_data['title']) 94 + ). 95 + phutil_render_tag( 96 + 'div', 97 + array( 98 + 'class' => 'subtitle', 99 + ), 100 + phutil_escape_html($display_data['subtitle']) 101 + ); 102 + 103 + return array('header' => $header); 104 + } 105 + 106 + private function renderMessagePaneContent() { 107 + require_celerity_resource('conpherence-message-pane-css'); 108 + $user = $this->getRequest()->getUser(); 109 + $conpherence = $this->getConpherence(); 110 + $handles = $conpherence->getHandles(); 111 + $rendered_transactions = array(); 112 + 113 + $transactions = $conpherence->getTransactions(); 114 + foreach ($transactions as $transaction) { 115 + $rendered_transactions[] = id(new ConpherenceTransactionView()) 116 + ->setUser($user) 117 + ->setConpherenceTransaction($transaction) 118 + ->setHandles($handles) 119 + ->render(); 120 + } 121 + $transactions = implode(' ', $rendered_transactions); 122 + 123 + $form = 124 + id(new AphrontFormView()) 125 + ->setAction($this->getApplicationURI('update/'.$conpherence->getID().'/')) 126 + ->setFlexible(true) 127 + ->setUser($user) 128 + ->addHiddenInput('action', 'message') 129 + ->appendChild( 130 + id(new PhabricatorRemarkupControl()) 131 + ->setUser($user) 132 + ->setName('text') 133 + ) 134 + ->appendChild( 135 + id(new AphrontFormSubmitControl()) 136 + ->setValue(pht('Pontificate')) 137 + )->render(); 138 + 139 + return array( 140 + 'messages' => $transactions, 141 + 'form' => $form 142 + ); 143 + 144 + } 145 + 146 + private function renderWidgetPaneContent() { 147 + require_celerity_resource('conpherence-widget-pane-css'); 148 + Javelin::initBehavior( 149 + 'conpherence-widget-pane', 150 + array( 151 + 'widgetRegistery' => array( 152 + 'widgets-files' => 1, 153 + 'widgets-tasks' => 1, 154 + 'widgets-calendar' => 1, 155 + ) 156 + ) 157 + ); 158 + 159 + $conpherence = $this->getConpherence(); 160 + 161 + $widgets = phutil_render_tag( 162 + 'div', 163 + array( 164 + 'class' => 'widgets-header' 165 + ), 166 + javelin_render_tag( 167 + 'a', 168 + array( 169 + 'sigil' => 'conpherence-change-widget', 170 + 'meta' => array('widget' => 'widgets-files') 171 + ), 172 + pht('Files') 173 + ).' | '. 174 + javelin_render_tag( 175 + 'a', 176 + array( 177 + 'sigil' => 'conpherence-change-widget', 178 + 'meta' => array('widget' => 'widgets-tasks') 179 + ), 180 + pht('Tasks') 181 + ).' | '. 182 + javelin_render_tag( 183 + 'a', 184 + array( 185 + 'sigil' => 'conpherence-change-widget', 186 + 'meta' => array('widget' => 'widgets-calendar') 187 + ), 188 + pht('Calendar') 189 + ) 190 + ). 191 + phutil_render_tag( 192 + 'div', 193 + array( 194 + 'class' => 'widgets-body', 195 + 'id' => 'widgets-files', 196 + 'style' => 'display: none;' 197 + ), 198 + $this->renderFilesWidgetPaneContent() 199 + ). 200 + phutil_render_tag( 201 + 'div', 202 + array( 203 + 'class' => 'widgets-body', 204 + 'id' => 'widgets-tasks', 205 + ), 206 + $this->renderTaskWidgetPaneContent() 207 + ). 208 + phutil_render_tag( 209 + 'div', 210 + array( 211 + 'class' => 'widgets-body', 212 + 'id' => 'widgets-calendar', 213 + 'style' => 'display: none;' 214 + ), 215 + $this->renderCalendarWidgetPaneContent() 216 + ); 217 + 218 + return array('widgets' => $widgets); 219 + } 220 + 221 + private function renderFilesWidgetPaneContent() { 222 + $conpherence = $this->getConpherence(); 223 + $widget_data = $conpherence->getWidgetData(); 224 + $files = $widget_data['files']; 225 + 226 + $table_data = array(); 227 + foreach ($files as $file) { 228 + $thumb = $file->getThumb60x45URI(); 229 + $table_data[] = array( 230 + phutil_render_tag( 231 + 'img', 232 + array( 233 + 'src' => $thumb 234 + ), 235 + '' 236 + ), 237 + $file->getName() 238 + ); 239 + } 240 + $header = id(new PhabricatorHeaderView()) 241 + ->setHeader(pht('Attached Files')); 242 + $table = id(new AphrontTableView($table_data)) 243 + ->setNoDataString(pht('No files attached to conpherence.')) 244 + ->setHeaders(array('', pht('Name'))) 245 + ->setColumnClasses(array('', 'wide')); 246 + return $header->render() . $table->render(); 247 + } 248 + 249 + private function renderTaskWidgetPaneContent() { 250 + $conpherence = $this->getConpherence(); 251 + $widget_data = $conpherence->getWidgetData(); 252 + $tasks = $widget_data['tasks']; 253 + $priority_map = ManiphestTaskPriority::getTaskPriorityMap(); 254 + $handles = $conpherence->getHandles(); 255 + $content = array(); 256 + foreach ($tasks as $owner_phid => $actual_tasks) { 257 + $handle = $handles[$owner_phid]; 258 + $content[] = id(new PhabricatorHeaderView()) 259 + ->setHeader($handle->getName()) 260 + ->render(); 261 + $actual_tasks = msort($actual_tasks, 'getPriority'); 262 + $actual_tasks = array_reverse($actual_tasks); 263 + $data = array(); 264 + foreach ($actual_tasks as $task) { 265 + $data[] = array( 266 + idx($priority_map, $task->getPriority(), pht('???')), 267 + phutil_render_tag( 268 + 'a', 269 + array( 270 + 'href' => '/T'.$task->getID() 271 + ), 272 + phutil_escape_html($task->getTitle()) 273 + ) 274 + ); 275 + } 276 + $table = id(new AphrontTableView($data)) 277 + ->setNoDataString(pht('No open tasks.')) 278 + ->setHeaders(array(pht('Pri'), pht('Title'))) 279 + ->setColumnClasses(array('', 'wide')); 280 + $content[] = $table->render(); 281 + } 282 + return implode('', $content); 283 + } 284 + 285 + private function renderCalendarWidgetPaneContent() { 286 + $header = id(new PhabricatorHeaderView()) 287 + ->setHeader(pht('Calendar')); 288 + return $header->render() . 'TODO'; 289 + } 290 + 291 + }
+175
src/applications/conpherence/editor/ConpherenceEditor.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor { 7 + 8 + public function getTransactionTypes() { 9 + $types = parent::getTransactionTypes(); 10 + 11 + $types[] = PhabricatorTransactions::TYPE_COMMENT; 12 + 13 + $types[] = ConpherenceTransactionType::TYPE_TITLE; 14 + $types[] = ConpherenceTransactionType::TYPE_PICTURE; 15 + $types[] = ConpherenceTransactionType::TYPE_PARTICIPANTS; 16 + $types[] = ConpherenceTransactionType::TYPE_FILES; 17 + 18 + return $types; 19 + } 20 + 21 + protected function getCustomTransactionOldValue( 22 + PhabricatorLiskDAO $object, 23 + PhabricatorApplicationTransaction $xaction) { 24 + 25 + switch ($xaction->getTransactionType()) { 26 + case ConpherenceTransactionType::TYPE_TITLE: 27 + return $object->getTitle(); 28 + case ConpherenceTransactionType::TYPE_PICTURE: 29 + return $object->getImagePHID(); 30 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 31 + return $object->getParticipantPHIDs(); 32 + case ConpherenceTransactionType::TYPE_FILES: 33 + return $object->getFilePHIDs(); 34 + } 35 + } 36 + 37 + protected function getCustomTransactionNewValue( 38 + PhabricatorLiskDAO $object, 39 + PhabricatorApplicationTransaction $xaction) { 40 + 41 + switch ($xaction->getTransactionType()) { 42 + case ConpherenceTransactionType::TYPE_TITLE: 43 + case ConpherenceTransactionType::TYPE_PICTURE: 44 + return $xaction->getNewValue(); 45 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 46 + case ConpherenceTransactionType::TYPE_FILES: 47 + return $this->getPHIDTransactionNewValue($xaction); 48 + } 49 + } 50 + 51 + protected function applyCustomInternalTransaction( 52 + PhabricatorLiskDAO $object, 53 + PhabricatorApplicationTransaction $xaction) { 54 + 55 + switch ($xaction->getTransactionType()) { 56 + case ConpherenceTransactionType::TYPE_TITLE: 57 + $object->setTitle($xaction->getNewValue()); 58 + break; 59 + case ConpherenceTransactionType::TYPE_PICTURE: 60 + $object->setImagePHID($xaction->getNewValue()); 61 + break; 62 + } 63 + } 64 + 65 + /** 66 + * For now this only supports adding more files and participants. 67 + */ 68 + protected function applyCustomExternalTransaction( 69 + PhabricatorLiskDAO $object, 70 + PhabricatorApplicationTransaction $xaction) { 71 + 72 + switch ($xaction->getTransactionType()) { 73 + case ConpherenceTransactionType::TYPE_FILES: 74 + $editor = id(new PhabricatorEdgeEditor()) 75 + ->setActor($this->getActor()); 76 + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE; 77 + foreach ($xaction->getNewValue() as $file_phid) { 78 + $editor->addEdge( 79 + $object->getPHID(), 80 + $edge_type, 81 + $file_phid 82 + ); 83 + } 84 + $editor->save(); 85 + break; 86 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 87 + foreach ($xaction->getNewValue() as $participant) { 88 + if ($participant == $this->getActor()->getPHID()) { 89 + $status = ConpherenceParticipationStatus::UP_TO_DATE; 90 + } else { 91 + $status = ConpherenceParticipationStatus::BEHIND; 92 + } 93 + id(new ConpherenceParticipant()) 94 + ->setConpherencePHID($object->getPHID()) 95 + ->setParticipantPHID($participant) 96 + ->setParticipationStatus($status) 97 + ->setDateTouched(time()) 98 + ->setBehindTransactionPHID($xaction->getPHID()) 99 + ->save(); 100 + } 101 + break; 102 + } 103 + } 104 + 105 + protected function mergeTransactions( 106 + PhabricatorApplicationTransaction $u, 107 + PhabricatorApplicationTransaction $v) { 108 + 109 + $type = $u->getTransactionType(); 110 + switch ($type) { 111 + case ConpherenceTransactionType::TYPE_TITLE: 112 + case ConpherenceTransactionType::TYPE_PICTURE: 113 + return $v; 114 + case ConpherenceTransactionType::TYPE_FILES: 115 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 116 + return $this->mergePHIDTransactions($u, $v); 117 + } 118 + 119 + return parent::mergeTransactions($u, $v); 120 + } 121 + 122 + protected function supportsMail() { 123 + return false; 124 + } 125 + 126 + /* TODO 127 + 128 + protected function buildReplyHandler(PhabricatorLiskDAO $object) { 129 + return id(new ConpherenceReplyHandler()) 130 + ->setMailReceiver($object); 131 + } 132 + 133 + protected function buildMailTemplate(PhabricatorLiskDAO $object) { 134 + $id = $object->getID(); 135 + $title = $object->getTitle(); 136 + $phid = $object->getPHID(); 137 + $original_name = $object->getOriginalName(); 138 + 139 + return id(new PhabricatorMetaMTAMail()) 140 + ->setSubject("C{$id}: {$title}") 141 + ->addHeader('Thread-Topic', "C{$id}: {$phid}"); 142 + } 143 + 144 + protected function getMailTo(PhabricatorLiskDAO $object) { 145 + $participants = $object->getParticipants(); 146 + $participants[$this->requireActor()->getPHID()] = true; 147 + return array_keys($participants); 148 + } 149 + 150 + protected function buildMailBody( 151 + PhabricatorLiskDAO $object, 152 + array $xactions) { 153 + 154 + $body = parent::buildMailBody($object, $xactions); 155 + $body->addTextSection( 156 + pht('CONPHERENCE DETAIL'), 157 + PhabricatorEnv::getProductionURI('/conpherence/'.$object->getID().'/')); 158 + 159 + return $body; 160 + } 161 + 162 + protected function getMailSubjectPrefix() { 163 + return PhabricatorEnv::getEnvConfig('metamta.conpherence.subject-prefix'); 164 + } 165 + */ 166 + 167 + protected function supportsFeed() { 168 + return false; 169 + } 170 + 171 + protected function supportsSearch() { 172 + return false; 173 + } 174 + 175 + }
+98
src/applications/conpherence/query/ConpherenceParticipantQuery.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceParticipantQuery 7 + extends PhabricatorOffsetPagedQuery { 8 + 9 + private $conpherencePHIDs; 10 + private $participantPHIDs; 11 + private $dateTouched; 12 + private $dateTouchedSort; 13 + private $participationStatus; 14 + 15 + public function withConpherencePHIDs(array $phids) { 16 + $this->conpherencePHIDs = $phids; 17 + return $this; 18 + } 19 + 20 + public function withParticipantPHIDs(array $phids) { 21 + $this->participantPHIDs = $phids; 22 + return $this; 23 + } 24 + 25 + public function withDateTouched($date, $sort = null) { 26 + $this->dateTouched = $date; 27 + $this->dateTouchedSort = $sort ? $sort : '<'; 28 + return $this; 29 + } 30 + 31 + public function withParticipationStatus($participation_status) { 32 + $this->participationStatus = $participation_status; 33 + return $this; 34 + } 35 + 36 + public function execute() { 37 + $table = new ConpherenceParticipant(); 38 + $conn_r = $table->establishConnection('r'); 39 + 40 + $data = queryfx_all( 41 + $conn_r, 42 + 'SELECT * FROM %T participant %Q %Q %Q', 43 + $table->getTableName(), 44 + $this->buildWhereClause($conn_r), 45 + $this->buildOrderClause($conn_r), 46 + $this->buildLimitClause($conn_r)); 47 + 48 + $participants = $table->loadAllFromArray($data); 49 + 50 + $participants = mpull($participants, null, 'getConpherencePHID'); 51 + 52 + return $participants; 53 + } 54 + 55 + private function buildWhereClause($conn_r) { 56 + $where = array(); 57 + 58 + if ($this->conpherencePHIDs) { 59 + $where[] = qsprintf( 60 + $conn_r, 61 + 'conpherencePHID IN (%Ls)', 62 + $this->conpherencePHIDs); 63 + } 64 + 65 + if ($this->participantPHIDs) { 66 + $where[] = qsprintf( 67 + $conn_r, 68 + 'participantPHID IN (%Ls)', 69 + $this->participantPHIDs); 70 + } 71 + 72 + if ($this->participationStatus !== null) { 73 + $where[] = qsprintf( 74 + $conn_r, 75 + 'participationStatus = %d', 76 + $this->participationStatus 77 + ); 78 + } 79 + 80 + if ($this->dateTouched) { 81 + if ($this->dateTouchedSort) { 82 + $where[] = qsprintf( 83 + $conn_r, 84 + 'dateTouched %Q %d', 85 + $this->dateTouchedSort, 86 + $this->dateTouched 87 + ); 88 + } 89 + } 90 + 91 + return $this->formatWhereClause($where); 92 + } 93 + 94 + private function buildOrderClause(AphrontDatabaseConnection $conn_r) { 95 + return 'ORDER BY dateTouched DESC'; 96 + } 97 + 98 + }
+182
src/applications/conpherence/query/ConpherenceThreadQuery.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceThreadQuery 7 + extends PhabricatorCursorPagedPolicyAwareQuery { 8 + 9 + private $phids; 10 + private $ids; 11 + private $needWidgetData; 12 + 13 + public function needWidgetData($need_widget_data) { 14 + $this->needWidgetData = $need_widget_data; 15 + return $this; 16 + } 17 + 18 + public function withIDs(array $ids) { 19 + $this->ids = $ids; 20 + return $this; 21 + } 22 + 23 + public function withPHIDs(array $phids) { 24 + $this->phids = $phids; 25 + return $this; 26 + } 27 + 28 + public function loadPage() { 29 + $table = new ConpherenceThread(); 30 + $conn_r = $table->establishConnection('r'); 31 + 32 + $data = queryfx_all( 33 + $conn_r, 34 + 'SELECT conpherence_thread.* FROM %T conpherence_thread %Q %Q %Q', 35 + $table->getTableName(), 36 + $this->buildWhereClause($conn_r), 37 + $this->buildOrderClause($conn_r), 38 + $this->buildLimitClause($conn_r)); 39 + 40 + $conpherences = $table->loadAllFromArray($data); 41 + 42 + if ($conpherences) { 43 + $conpherences = mpull($conpherences, null, 'getPHID'); 44 + $this->loadParticipants($conpherences); 45 + $this->loadTransactionsAndHandles($conpherences); 46 + $this->loadFilePHIDs($conpherences); 47 + if ($this->needWidgetData) { 48 + $this->loadWidgetData($conpherences); 49 + } 50 + } 51 + 52 + return $conpherences; 53 + } 54 + 55 + protected function buildWhereClause($conn_r) { 56 + $where = array(); 57 + 58 + $where[] = $this->buildPagingClause($conn_r); 59 + 60 + if ($this->ids) { 61 + $where[] = qsprintf( 62 + $conn_r, 63 + 'id IN (%Ld)', 64 + $this->ids); 65 + } 66 + 67 + if ($this->phids) { 68 + $where[] = qsprintf( 69 + $conn_r, 70 + 'phid IN (%Ls)', 71 + $this->phids); 72 + } 73 + 74 + return $this->formatWhereClause($where); 75 + } 76 + 77 + private function loadParticipants(array $conpherences) { 78 + $participants = id(new ConpherenceParticipant()) 79 + ->loadAllWhere('conpherencePHID IN (%Ls)', array_keys($conpherences)); 80 + $map = mgroup($participants, 'getConpherencePHID'); 81 + foreach ($map as $conpherence_phid => $conpherence_participants) { 82 + $current_conpherence = $conpherences[$conpherence_phid]; 83 + $conpherence_participants = mpull( 84 + $conpherence_participants, 85 + null, 86 + 'getParticipantPHID' 87 + ); 88 + $current_conpherence->attachParticipants($conpherence_participants); 89 + } 90 + 91 + return $this; 92 + } 93 + 94 + private function loadTransactionsAndHandles(array $conpherences) { 95 + $transactions = id(new ConpherenceTransactionQuery()) 96 + ->setViewer($this->getViewer()) 97 + ->withObjectPHIDs(array_keys($conpherences)) 98 + ->needHandles(true) 99 + ->loadPage(); 100 + $transactions = mgroup($transactions, 'getObjectPHID'); 101 + foreach ($conpherences as $phid => $conpherence) { 102 + $current_transactions = $transactions[$phid]; 103 + $handles = array(); 104 + foreach ($current_transactions as $transaction) { 105 + $handles += $transaction->getHandles(); 106 + } 107 + $conpherence->attachHandles($handles); 108 + $conpherence->attachTransactions($transactions[$phid]); 109 + } 110 + return $this; 111 + } 112 + 113 + private function loadFilePHIDs(array $conpherences) { 114 + $edge_type = PhabricatorEdgeConfig::TYPE_OBJECT_HAS_FILE; 115 + $file_edges = id(new PhabricatorEdgeQuery()) 116 + ->withSourcePHIDs(array_keys($conpherences)) 117 + ->withEdgeTypes(array($edge_type)) 118 + ->execute(); 119 + foreach ($file_edges as $conpherence_phid => $data) { 120 + $conpherence = $conpherences[$conpherence_phid]; 121 + $conpherence->attachFilePHIDs(array_keys($data[$edge_type])); 122 + } 123 + return $this; 124 + } 125 + 126 + private function loadWidgetData(array $conpherences) { 127 + $participant_phids = array(); 128 + $file_phids = array(); 129 + foreach ($conpherences as $conpherence) { 130 + $participant_phids[] = array_keys($conpherence->getParticipants()); 131 + $file_phids[] = $conpherence->getFilePHIDs(); 132 + } 133 + $participant_phids = array_mergev($participant_phids); 134 + $file_phids = array_mergev($file_phids); 135 + 136 + // open tasks of all participants 137 + $tasks = id(new ManiphestTaskQuery()) 138 + ->withOwners($participant_phids) 139 + ->withStatus(ManiphestTaskQuery::STATUS_OPEN) 140 + ->execute(); 141 + $tasks = mgroup($tasks, 'getOwnerPHID'); 142 + 143 + // statuses of everyone currently in the conpherence 144 + // until the beginning of the next work week. 145 + // NOTE: this is a bit boring on the weekends. 146 + $end_of_week = phabricator_format_local_time( 147 + strtotime('Monday midnight'), 148 + $this->getViewer(), 149 + 'U' 150 + ); 151 + $statuses = id(new PhabricatorUserStatus()) 152 + ->loadAllWhere( 153 + 'userPHID in (%Ls) AND dateTo <= %d', 154 + $participant_phids, 155 + $end_of_week 156 + ); 157 + $statuses = mgroup($statuses, 'getUserPHID'); 158 + 159 + // attached files 160 + $files = array(); 161 + if ($file_phids) { 162 + $files = id(new PhabricatorFileQuery()) 163 + ->setViewer($this->getViewer()) 164 + ->withPHIDs($file_phids) 165 + ->execute(); 166 + $files = mpull($files, null, 'getPHID'); 167 + } 168 + 169 + foreach ($conpherences as $phid => $conpherence) { 170 + $participant_phids = array_keys($conpherence->getParticipants()); 171 + $widget_data = array( 172 + 'tasks' => array_select_keys($tasks, $participant_phids), 173 + 'statuses' => array_select_keys($statuses, $participant_phids), 174 + 'files' => array_select_keys($files, $conpherence->getFilePHIDs()), 175 + ); 176 + $conpherence->attachWidgetData($widget_data); 177 + } 178 + 179 + return $this; 180 + } 181 + 182 + }
+13
src/applications/conpherence/query/ConpherenceTransactionQuery.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceTransactionQuery 7 + extends PhabricatorApplicationTransactionQuery { 8 + 9 + protected function getTemplateApplicationTransaction() { 10 + return new ConpherenceTransaction(); 11 + } 12 + 13 + }
+12
src/applications/conpherence/storage/ConpherenceDAO.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + abstract class ConpherenceDAO extends PhabricatorLiskDAO { 7 + 8 + public function getApplicationName() { 9 + return 'conpherence'; 10 + } 11 + 12 + }
+27
src/applications/conpherence/storage/ConpherenceParticipant.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceParticipant extends ConpherenceDAO { 7 + 8 + protected $participantPHID; 9 + protected $conpherencePHID; 10 + protected $participationStatus; 11 + protected $behindTransactionPHID; 12 + protected $dateTouched; 13 + 14 + public function markUpToDate() { 15 + if (!$this->isUpToDate()) { 16 + $this->setParticipationStatus(ConpherenceParticipationStatus::UP_TO_DATE); 17 + $this->save(); 18 + } 19 + return $this; 20 + } 21 + 22 + public function isUpToDate() { 23 + return $this->getParticipationStatus() == 24 + ConpherenceParticipationStatus::UP_TO_DATE; 25 + } 26 + 27 + }
+237
src/applications/conpherence/storage/ConpherenceThread.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceThread extends ConpherenceDAO 7 + implements PhabricatorPolicyInterface { 8 + 9 + protected $id; 10 + protected $phid; 11 + protected $title; 12 + protected $imagePHID; 13 + protected $mailKey; 14 + 15 + private $participants; 16 + private $transactions; 17 + private $handles; 18 + private $filePHIDs; 19 + private $widgetData; 20 + 21 + public function getConfiguration() { 22 + return array( 23 + self::CONFIG_AUX_PHID => true, 24 + ) + parent::getConfiguration(); 25 + } 26 + 27 + public function generatePHID() { 28 + return PhabricatorPHID::generateNewPHID( 29 + PhabricatorPHIDConstants::PHID_TYPE_CONP); 30 + } 31 + 32 + public function save() { 33 + if (!$this->getMailKey()) { 34 + $this->setMailKey(Filesystem::readRandomCharacters(20)); 35 + } 36 + return parent::save(); 37 + } 38 + 39 + public function attachParticipants(array $participants) { 40 + assert_instances_of($participants, 'ConpherenceParticipant'); 41 + $this->participants = $participants; 42 + return $this; 43 + } 44 + public function getParticipants() { 45 + if ($this->participants === null) { 46 + throw new Exception( 47 + 'You must attachParticipants first!' 48 + ); 49 + } 50 + return $this->participants; 51 + } 52 + public function getParticipant($phid) { 53 + $participants = $this->getParticipants(); 54 + return $participants[$phid]; 55 + } 56 + public function getParticipantPHIDs() { 57 + $participants = $this->getParticipants(); 58 + return array_keys($participants); 59 + } 60 + 61 + public function attachHandles(array $handles) { 62 + assert_instances_of($handles, 'PhabricatorObjectHandle'); 63 + $this->handles = $handles; 64 + return $this; 65 + } 66 + public function getHandles() { 67 + if ($this->handles === null) { 68 + throw new Exception( 69 + 'You must attachHandles first!' 70 + ); 71 + } 72 + return $this->handles; 73 + } 74 + 75 + public function attachTransactions(array $transactions) { 76 + assert_instances_of($transactions, 'ConpherenceTransaction'); 77 + $this->transactions = $transactions; 78 + return $this; 79 + } 80 + public function getTransactions() { 81 + if ($this->transactions === null) { 82 + throw new Exception( 83 + 'You must attachTransactions first!' 84 + ); 85 + } 86 + return $this->transactions; 87 + } 88 + 89 + public function attachFilePHIDs(array $file_phids) { 90 + $this->filePHIDs = $file_phids; 91 + return $this; 92 + } 93 + public function getFilePHIDs() { 94 + if ($this->filePHIDs === null) { 95 + throw new Exception( 96 + 'You must attachFilePHIDs first!' 97 + ); 98 + } 99 + return $this->filePHIDs; 100 + } 101 + 102 + public function attachWidgetData(array $widget_data) { 103 + $this->widgetData = $widget_data; 104 + return $this; 105 + } 106 + public function getWidgetData() { 107 + if ($this->widgetData === null) { 108 + throw new Exception( 109 + 'You must attachWidgetData first!' 110 + ); 111 + } 112 + return $this->widgetData; 113 + } 114 + 115 + public function loadImageURI() { 116 + $src_phid = $this->getImagePHID(); 117 + 118 + if ($src_phid) { 119 + $file = id(new PhabricatorFile())->loadOneWhere('phid = %s', $src_phid); 120 + if ($file) { 121 + return $file->getBestURI(); 122 + } 123 + } 124 + 125 + return PhabricatorUser::getDefaultProfileImageURI(); 126 + } 127 + 128 + public function getDisplayData(PhabricatorUser $user) { 129 + $transactions = $this->getTransactions(); 130 + $latest_transaction = end($transactions); 131 + $latest_participant = $latest_transaction->getAuthorPHID(); 132 + $handles = $this->getHandles(); 133 + $latest_handle = $handles[$latest_participant]; 134 + if ($this->getImagePHID()) { 135 + $img_src = $this->loadImageURI(); 136 + } else { 137 + $img_src = $latest_handle->getImageURI(); 138 + } 139 + $title = $this->getTitle(); 140 + if (!$title) { 141 + $title = $latest_handle->getName(); 142 + unset($handles[$latest_participant]); 143 + } 144 + unset($handles[$user->getPHID()]); 145 + 146 + $subtitle = ''; 147 + $count = 0; 148 + $final = false; 149 + foreach ($handles as $handle) { 150 + if ($handle->getType() != PhabricatorPHIDConstants::PHID_TYPE_USER) { 151 + continue; 152 + } 153 + if ($subtitle) { 154 + if ($final) { 155 + $subtitle .= '...'; 156 + break; 157 + } else { 158 + $subtitle .= ', '; 159 + } 160 + } 161 + $subtitle .= $handle->getName(); 162 + $count++; 163 + $final = $count == 3; 164 + } 165 + 166 + $participants = $this->getParticipants(); 167 + $user_participation = $participants[$user->getPHID()]; 168 + $unread_count = 0; 169 + $max_count = 10; 170 + $snippet = null; 171 + if (!$user_participation->isUpToDate()) { 172 + $behind_transaction_phid = 173 + $user_participation->getBehindTransactionPHID(); 174 + } else { 175 + $behind_transaction_phid = null; 176 + } 177 + foreach (array_reverse($transactions) as $transaction) { 178 + switch ($transaction->getTransactionType()) { 179 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 180 + case ConpherenceTransactionType::TYPE_TITLE: 181 + case ConpherenceTransactionType::TYPE_PICTURE: 182 + continue 2; 183 + case PhabricatorTransactions::TYPE_COMMENT: 184 + if ($snippet === null) { 185 + $snippet = phutil_utf8_shorten( 186 + $transaction->getComment()->getContent(), 187 + 48 188 + ); 189 + } 190 + // fallthrough intentionally here 191 + case ConpherenceTransactionType::TYPE_FILES: 192 + default: 193 + if ($behind_transaction_phid && 194 + $transaction->getPHID() != $behind_transaction_phid) { 195 + $unread_count++; 196 + } 197 + if ($unread_count > $max_count) { 198 + break 2; 199 + } 200 + break; 201 + } 202 + if ($snippet && !$behind_transaction_phid) { 203 + break; 204 + } 205 + } 206 + if ($unread_count > $max_count) { 207 + $unread_count = $max_count.'+'; 208 + } 209 + 210 + return array( 211 + 'title' => $title, 212 + 'subtitle' => $subtitle, 213 + 'unread_count' => $unread_count, 214 + 'epoch' => $latest_transaction->getDateCreated(), 215 + 'image' => $img_src, 216 + 'snippet' => $snippet, 217 + ); 218 + } 219 + 220 + /* -( PhabricatorPolicyInterface Implementation )-------------------------- */ 221 + public function getCapabilities() { 222 + return array( 223 + PhabricatorPolicyCapability::CAN_VIEW, 224 + PhabricatorPolicyCapability::CAN_EDIT, 225 + ); 226 + } 227 + 228 + public function getPolicy($capability) { 229 + return PhabricatorPolicies::POLICY_NOONE; 230 + } 231 + 232 + public function hasAutomaticCapability($capability, PhabricatorUser $user) { 233 + $participants = $this->getParticipants(); 234 + return isset($participants[$user->getPHID()]); 235 + } 236 + 237 + }
+124
src/applications/conpherence/storage/ConpherenceTransaction.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceTransaction extends PhabricatorApplicationTransaction { 7 + 8 + public function getApplicationName() { 9 + return 'conpherence'; 10 + } 11 + 12 + public function getApplicationTransactionType() { 13 + return PhabricatorPHIDConstants::PHID_TYPE_CONP; 14 + } 15 + 16 + public function getApplicationTransactionCommentObject() { 17 + return new ConpherenceTransactionComment(); 18 + } 19 + 20 + public function getApplicationObjectTypeName() { 21 + return pht('conpherence'); 22 + } 23 + 24 + public function shouldHide() { 25 + $old = $this->getOldValue(); 26 + 27 + switch ($this->getTransactionType()) { 28 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 29 + return ($old === null); 30 + case ConpherenceTransactionType::TYPE_TITLE: 31 + case ConpherenceTransactionType::TYPE_PICTURE: 32 + case ConpherenceTransactionType::TYPE_FILES: 33 + return false; 34 + } 35 + 36 + return parent::shouldHide(); 37 + } 38 + 39 + public function getTitle() { 40 + $author_phid = $this->getAuthorPHID(); 41 + 42 + $old = $this->getOldValue(); 43 + $new = $this->getNewValue(); 44 + 45 + switch ($this->getTransactionType()) { 46 + case ConpherenceTransactionType::TYPE_TITLE: 47 + if ($old) { 48 + $title = pht( 49 + '%s renamed this conpherence from "%s" to "%s".', 50 + $this->renderHandleLink($author_phid), 51 + phutil_escape_html($old), 52 + phutil_escape_html($new)); 53 + } else { 54 + $title = pht( 55 + '%s named this conpherence "%s".', 56 + $this->renderHandleLink($author_phid), 57 + phutil_escape_html($new)); 58 + } 59 + return $title; 60 + case ConpherenceTransactionType::TYPE_FILES: 61 + return pht( 62 + '%s updated the conpherence files.', 63 + $this->renderHandleLink($author_phid)); 64 + case ConpherenceTransactionType::TYPE_PICTURE: 65 + return pht( 66 + '%s updated the conpherence image.', 67 + $this->renderHandleLink($author_phid)); 68 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 69 + $add = array_diff($new, $old); 70 + $rem = array_diff($old, $new); 71 + 72 + if ($add && $rem) { 73 + $title = pht( 74 + '%s edited participant(s), added %d: %s; removed %d: %s.', 75 + $this->renderHandleLink($author_phid), 76 + count($add), 77 + $this->renderHandleList($add), 78 + count($rem), 79 + $this->renderHandleList($rem)); 80 + } else if ($add) { 81 + $title = pht( 82 + '%s added %d participant(s): %s.', 83 + $this->renderHandleLink($author_phid), 84 + count($add), 85 + $this->renderHandleList($add)); 86 + } else { 87 + $title = pht( 88 + '%s removed %d partipant(s): %s.', 89 + $this->renderHandleLink($author_phid), 90 + count($rem), 91 + $this->renderHandleList($rem)); 92 + } 93 + return $title; 94 + break; 95 + } 96 + 97 + return parent::getTitle(); 98 + } 99 + 100 + public function getRequiredHandlePHIDs() { 101 + $phids = parent::getRequiredHandlePHIDs(); 102 + 103 + $old = $this->getOldValue(); 104 + $new = $this->getNewValue(); 105 + 106 + $phids[] = $this->getAuthorPHID(); 107 + switch ($this->getTransactionType()) { 108 + case ConpherenceTransactionType::TYPE_PICTURE: 109 + $phids[] = $new; 110 + break; 111 + case ConpherenceTransactionType::TYPE_TITLE: 112 + break; 113 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 114 + case ConpherenceTransactionType::TYPE_FILES: 115 + $phids = array_merge($phids, $this->getOldValue()); 116 + $phids = array_merge($phids, $this->getNewValue()); 117 + break; 118 + 119 + } 120 + 121 + return $phids; 122 + } 123 + 124 + }
+15
src/applications/conpherence/storage/ConpherenceTransactionComment.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceTransactionComment 7 + extends PhabricatorApplicationTransactionComment { 8 + 9 + protected $conpherencePHID; 10 + 11 + public function getApplicationTransactionObject() { 12 + return new ConpherenceTransaction(); 13 + } 14 + 15 + }
+144
src/applications/conpherence/view/ConpherenceMenuItemView.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceMenuItemView extends AphrontTagView { 7 + 8 + private $title; 9 + private $subtitle; 10 + private $imageURI; 11 + private $href; 12 + private $messageText; 13 + private $epoch; 14 + private $unreadCount; 15 + 16 + public function setUnreadCount($unread_count) { 17 + $this->unreadCount = $unread_count; 18 + return $this; 19 + } 20 + public function getUnreadCount() { 21 + return $this->unreadCount; 22 + } 23 + 24 + public function setMessageText($message_text) { 25 + $this->messageText = $message_text; 26 + return $this; 27 + } 28 + public function getMessageText() { 29 + return $this->messageText; 30 + } 31 + 32 + public function setEpoch($epoch) { 33 + $this->epoch = $epoch; 34 + return $this; 35 + } 36 + public function getEpoch() { 37 + return $this->epoch; 38 + } 39 + 40 + public function setHref($href) { 41 + $this->href = $href; 42 + return $this; 43 + } 44 + public function getHref() { 45 + return $this->href; 46 + } 47 + 48 + public function setImageURI($image_uri) { 49 + $this->imageURI = $image_uri; 50 + return $this; 51 + } 52 + public function getImageURI() { 53 + return $this->imageURI; 54 + } 55 + 56 + public function setSubtitle($subtitle) { 57 + $this->subtitle = $subtitle; 58 + return $this; 59 + } 60 + public function getSubtitle() { 61 + return $this->subtitle; 62 + } 63 + 64 + public function setTitle($title) { 65 + $this->title = $title; 66 + return $this; 67 + } 68 + 69 + public function getTitle() { 70 + return $this->title; 71 + } 72 + 73 + protected function getTagName() { 74 + return 'a'; 75 + } 76 + 77 + protected function getTagAttributes() { 78 + $classes = array('conpherence-menu-item-view'); 79 + return array( 80 + 'class' => $classes, 81 + 'href' => $this->href, 82 + ); 83 + } 84 + 85 + protected function getTagContent() { 86 + $image = null; 87 + if ($this->imageURI) { 88 + $image = phutil_render_tag( 89 + 'span', 90 + array( 91 + 'class' => 'conpherence-menu-item-image', 92 + 'style' => 'background-image: url('.$this->imageURI.');' 93 + ), 94 + ''); 95 + } 96 + $title = null; 97 + if ($this->title) { 98 + $title = phutil_render_tag( 99 + 'span', 100 + array( 101 + 'class' => 'conpherence-menu-item-title', 102 + ), 103 + phutil_escape_html($this->title)); 104 + } 105 + $subtitle = null; 106 + if ($this->subtitle) { 107 + $subtitle = phutil_render_tag( 108 + 'span', 109 + array( 110 + 'class' => 'conpherence-menu-item-subtitle', 111 + ), 112 + phutil_escape_html($this->subtitle)); 113 + } 114 + $message = null; 115 + if ($this->messageText) { 116 + $message = phutil_render_tag( 117 + 'span', 118 + array( 119 + 'class' => 'conpherence-menu-item-message-text' 120 + ), 121 + phutil_escape_html($this->messageText)); 122 + } 123 + $epoch = null; 124 + if ($this->epoch) { 125 + $epoch = phutil_render_tag( 126 + 'span', 127 + array( 128 + 'class' => 'conpherence-menu-item-date', 129 + ), 130 + phabricator_relative_date($this->epoch, $this->user)); 131 + } 132 + $unread_count = null; 133 + if ($this->unreadCount) { 134 + $unread_count = phutil_render_tag( 135 + 'span', 136 + array( 137 + 'class' => 'conpherence-menu-item-unread-count' 138 + ), 139 + $this->unreadCount); 140 + } 141 + 142 + return $image.$title.$subtitle.$message.$epoch.$unread_count; 143 + } 144 + }
+98
src/applications/conpherence/view/ConpherenceTransactionView.php
··· 1 + <?php 2 + 3 + /** 4 + * @group conpherence 5 + */ 6 + final class ConpherenceTransactionView extends AphrontView { 7 + 8 + private $conpherenceTransaction; 9 + private $handles; 10 + 11 + public function setHandles(array $handles) { 12 + assert_instances_of($handles, 'PhabricatorObjectHandle'); 13 + $this->handles = $handles; 14 + return $this; 15 + } 16 + public function getHandles() { 17 + return $this->handles; 18 + } 19 + 20 + public function setConpherenceTransaction(ConpherenceTransaction $tx) { 21 + $this->conpherenceTransaction = $tx; 22 + return $this; 23 + } 24 + private function getConpherenceTransaction() { 25 + return $this->conpherenceTransaction; 26 + } 27 + 28 + public function render() { 29 + $transaction = $this->getConpherenceTransaction(); 30 + $handles = $this->getHandles(); 31 + $transaction->setHandles($handles); 32 + $author = $handles[$transaction->getAuthorPHID()]; 33 + $transaction_view = id(new PhabricatorTransactionView()) 34 + ->setUser($this->getUser()) 35 + ->setEpoch($transaction->getDateCreated()) 36 + ->setContentSource($transaction->getContentSource()); 37 + 38 + $content_class = null; 39 + switch ($transaction->getTransactionType()) { 40 + case ConpherenceTransactionType::TYPE_TITLE: 41 + $content = $transaction->getTitle(); 42 + $transaction_view->addClass('conpherence-edited'); 43 + break; 44 + case ConpherenceTransactionType::TYPE_FILES: 45 + $content = $transaction->getTitle(); 46 + break; 47 + case ConpherenceTransactionType::TYPE_PICTURE: 48 + $img = $transaction->getHandle($transaction->getNewValue()); 49 + $content = $transaction->getTitle() . 50 + phutil_render_tag( 51 + 'img', 52 + array( 53 + 'src' => $img->getImageURI() 54 + ) 55 + ); 56 + $transaction_view->addClass('conpherence-edited'); 57 + break; 58 + case ConpherenceTransactionType::TYPE_PARTICIPANTS: 59 + $content = $transaction->getTitle(); 60 + $transaction_view->addClass('conpherence-edited'); 61 + break; 62 + case PhabricatorTransactions::TYPE_COMMENT: 63 + $comment = $transaction->getComment(); 64 + $file_ids = 65 + PhabricatorMarkupEngine::extractFilePHIDsFromEmbeddedFiles( 66 + array($comment->getContent()) 67 + ); 68 + $markup_field = ConpherenceTransactionComment::MARKUP_FIELD_COMMENT; 69 + $engine = id(new PhabricatorMarkupEngine()) 70 + ->setViewer($this->getUser()); 71 + $engine->addObject( 72 + $comment, 73 + $markup_field 74 + ); 75 + $engine->process(); 76 + $content = $engine->getOutput( 77 + $comment, 78 + $markup_field 79 + ); 80 + $content_class = 'conpherence-message phabricator-remarkup'; 81 + $transaction_view 82 + ->setImageURI($author->getImageURI()) 83 + ->setActions(array($author->renderLink())); 84 + break; 85 + } 86 + 87 + $transaction_view 88 + ->appendChild(phutil_render_tag( 89 + 'div', 90 + array( 91 + 'class' => $content_class 92 + ), 93 + $content) 94 + ); 95 + 96 + return $transaction_view->render(); 97 + } 98 + }
+9 -1
src/applications/people/controller/PhabricatorPeopleProfileController.php
··· 39 39 } 40 40 $username = phutil_escape_uri($user->getUserName()); 41 41 42 + $external_arrow = "\xE2\x86\x97"; 43 + 44 + $conpherence_uri = 45 + new PhutilURI('/conpherence/new/?participant='.$user->getPHID()); 42 46 $nav = new AphrontSideNavFilterView(); 43 47 $nav->setBaseURI(new PhutilURI('/p/'.$username.'/')); 44 48 $nav->addFilter('feed', 'Feed'); 49 + $nav->addMenuItem( 50 + id(new PhabricatorMenuItemView()) 51 + ->setName(pht('Conpherence').' '.$external_arrow) 52 + ->setHref($conpherence_uri) 53 + ); 45 54 $nav->addFilter('about', 'About'); 46 55 $nav->addLabel('Activity'); 47 56 48 - $external_arrow = "\xE2\x86\x97"; 49 57 $nav->addFilter( 50 58 null, 51 59 "Revisions {$external_arrow}",
+1
src/applications/phid/PhabricatorPHIDConstants.php
··· 31 31 const PHID_TYPE_MOCK = 'MOCK'; 32 32 const PHID_TYPE_MCRO = 'MCRO'; 33 33 const PHID_TYPE_CONF = 'CONF'; 34 + const PHID_TYPE_CONP = 'CONP'; 34 35 35 36 const PHID_TYPE_XACT = 'XACT'; 36 37 const PHID_TYPE_XCMT = 'XCMT';
+3
src/applications/phid/handle/PhabricatorObjectHandleData.php
··· 414 414 $handle->setName($file->getName()); 415 415 $handle->setURI($file->getBestURI()); 416 416 $handle->setComplete(true); 417 + if ($file->isViewableImage()) { 418 + $handle->setImageURI($file->getBestURI()); 419 + } 417 420 } 418 421 $handles[$phid] = $handle; 419 422 }
+9
src/applications/transactions/storage/PhabricatorApplicationTransaction.php
··· 121 121 return $this->handles[$phid]; 122 122 } 123 123 124 + public function getHandles() { 125 + if ($this->handles === null) { 126 + throw new Exception( 127 + 'Transaction requires handles and it did not load them.' 128 + ); 129 + } 130 + return $this->handles; 131 + } 132 + 124 133 protected function renderHandleLink($phid) { 125 134 if ($this->renderingTarget == self::TARGET_HTML) { 126 135 return $this->getHandle($phid)->renderLink();
+7
src/infrastructure/edges/constants/PhabricatorEdgeConfig.php
··· 39 39 const TYPE_OBJECT_HAS_UNSUBSCRIBER = 23; 40 40 const TYPE_UNSUBSCRIBED_FROM_OBJECT = 24; 41 41 42 + const TYPE_OBJECT_HAS_FILE = 25; 43 + const TYPE_FILE_HAS_OBJECT = 26; 44 + 42 45 const TYPE_TEST_NO_CYCLE = 9000; 43 46 44 47 public static function getInverse($edge_type) { ··· 78 81 79 82 self::TYPE_OBJECT_HAS_UNSUBSCRIBER => self::TYPE_UNSUBSCRIBED_FROM_OBJECT, 80 83 self::TYPE_UNSUBSCRIBED_FROM_OBJECT => self::TYPE_OBJECT_HAS_UNSUBSCRIBER, 84 + 85 + self::TYPE_OBJECT_HAS_FILE => self::TYPE_FILE_HAS_OBJECT, 86 + self::TYPE_FILE_HAS_OBJECT => self::TYPE_OBJECT_HAS_FILE, 81 87 ); 82 88 83 89 return idx($map, $edge_type); ··· 109 115 PhabricatorPHIDConstants::PHID_TYPE_ANSW => 'PonderAnswer', 110 116 PhabricatorPHIDConstants::PHID_TYPE_MOCK => 'PholioMock', 111 117 PhabricatorPHIDConstants::PHID_TYPE_MCRO => 'PhabricatorFileImageMacro', 118 + PhabricatorPHIDConstants::PHID_TYPE_CONP => 'ConpherenceThread', 112 119 113 120 ); 114 121
+33
src/infrastructure/internationalization/PhabricatorBaseEnglishTranslation.php
··· 193 193 ), 194 194 ), 195 195 196 + '%s removed %d subscriber(s): %s.' => array( 197 + array( 198 + '%s removed a subscriber: %3$s.', 199 + '%s removed subscribers: %3$s.', 200 + ), 201 + array( 202 + '%s removed a subscriber: %3$s.', 203 + '%s removed subscribers: %3$s.', 204 + ), 205 + ), 206 + 207 + '%s added %d participant(s): %s.' => array( 208 + array( 209 + '%s added a participant: %3$s.', 210 + '%s added participants: %3$s.', 211 + ), 212 + array( 213 + '%s added a participant: %3$s.', 214 + '%s added participants: %3$s.', 215 + ), 216 + ), 217 + 218 + '%s removed %d participant(s): %s.' => array( 219 + array( 220 + '%s removed a participant: %3$s.', 221 + '%s removed participants: %3$s.', 222 + ), 223 + array( 224 + '%s removed a participant: %3$s.', 225 + '%s removed participants: %3$s.', 226 + ), 227 + ), 228 + 196 229 '%2$s Line(s)' => array( 197 230 '%2$s Line', 198 231 '%2$s Lines',
+16
src/infrastructure/markup/PhabricatorMarkupEngine.php
··· 480 480 return $mentions; 481 481 } 482 482 483 + public static function extractFilePHIDsFromEmbeddedFiles( 484 + array $content_blocks) { 485 + $files = array(); 486 + 487 + $engine = self::newDifferentialMarkupEngine(); 488 + 489 + foreach ($content_blocks as $content_block) { 490 + $engine->markupText($content_block); 491 + $ids = $engine->getTextMetadata( 492 + PhabricatorRemarkupRuleEmbedFile::KEY_EMBED_FILE_PHIDS, 493 + array()); 494 + $files += $ids; 495 + } 496 + 497 + return $files; 498 + } 483 499 484 500 /** 485 501 * Produce a corpus summary, in a way that shortens the underlying text
+4
src/infrastructure/markup/rule/PhabricatorRemarkupRuleEmbedFile.php
··· 7 7 extends PhutilRemarkupRule { 8 8 9 9 const KEY_RULE_EMBED_FILE = 'rule.embed.file'; 10 + const KEY_EMBED_FILE_PHIDS = 'phabricator.embedded-file-phids'; 10 11 11 12 public function apply($text) { 12 13 return preg_replace_callback( ··· 87 88 return; 88 89 } 89 90 91 + $file_phids = array(); 90 92 foreach ($metadata as $phid => $bundles) { 91 93 foreach ($bundles as $data) { 92 94 ··· 159 161 160 162 $engine->overwriteStoredText($data['token'], $embed); 161 163 } 164 + $file_phids[] = $phid; 162 165 } 166 + $engine->setTextMetadata(self::KEY_EMBED_FILE_PHIDS, $file_phids); 163 167 $engine->setTextMetadata($metadata_key, array()); 164 168 } 165 169
+8
src/infrastructure/storage/patch/PhabricatorBuiltinPatchList.php
··· 159 159 'type' => 'db', 160 160 'name' => 'pholio', 161 161 ), 162 + 'db.conpherence' => array( 163 + 'type' => 'db', 164 + 'name' => 'conpherence', 165 + ), 162 166 '0000.legacy.sql' => array( 163 167 'type' => 'sql', 164 168 'name' => $this->getPatchPath('0000.legacy.sql'), ··· 1084 1088 '20130103.filemetadata.sql' => array( 1085 1089 'type' => 'sql', 1086 1090 'name' => $this->getPatchPath('20130103.filemetadata.sql'), 1091 + ), 1092 + '20130111.conpherence.sql' => array( 1093 + 'type' => 'sql', 1094 + 'name' => $this->getPatchPath('20130111.conpherence.sql'), 1087 1095 ), 1088 1096 ); 1089 1097 }
+1
src/view/AphrontDialogView.php
··· 128 128 'action' => $this->submitURI, 129 129 'method' => 'post', 130 130 'id' => $this->formID, 131 + 'enctype' => $this->encType, 131 132 ); 132 133 133 134 $hidden_inputs = array();
+16 -6
src/view/form/control/AphrontFormTextAreaControl.php
··· 12 12 private $height; 13 13 private $readOnly; 14 14 private $customClass; 15 + private $placeHolder; 16 + 17 + public function setPlaceHolder($place_holder) { 18 + $this->placeHolder = $place_holder; 19 + return $this; 20 + } 21 + private function getPlaceHolder() { 22 + return $this->placeHolder; 23 + } 15 24 16 25 public function setHeight($height) { 17 26 $this->height = $height; ··· 55 64 return phutil_render_tag( 56 65 'textarea', 57 66 array( 58 - 'name' => $this->getName(), 59 - 'disabled' => $this->getDisabled() ? 'disabled' : null, 60 - 'readonly' => $this->getReadonly() ? 'readonly' : null, 61 - 'class' => $classes, 62 - 'style' => $this->getControlStyle(), 63 - 'id' => $this->getID(), 67 + 'name' => $this->getName(), 68 + 'disabled' => $this->getDisabled() ? 'disabled' : null, 69 + 'readonly' => $this->getReadonly() ? 'readonly' : null, 70 + 'class' => $classes, 71 + 'style' => $this->getControlStyle(), 72 + 'id' => $this->getID(), 73 + 'placeholder' => $this->getPlaceHolder(), 64 74 ), 65 75 phutil_escape_html($this->getValue())); 66 76 }
+13 -3
src/view/layout/AphrontSideNavFilterView.php
··· 25 25 private $menu; 26 26 private $crumbs; 27 27 private $classes = array(); 28 + private $menuID; 28 29 29 - public function addClass($class) { 30 - $this->classes[] = $class; 30 + public function setMenuID($menu_id) { 31 + $this->menuID = $menu_id; 31 32 return $this; 32 33 } 34 + public function getMenuID() { 35 + return $this->menuID; 36 + } 33 37 34 38 public function __construct() { 35 39 $this->menu = new PhabricatorMenuView(); 40 + } 41 + 42 + public function addClass($class) { 43 + $this->classes[] = $class; 44 + return $this; 36 45 } 37 46 38 47 public static function newFromMenu(PhabricatorMenuView $menu) { ··· 205 214 'class' => 'phabricator-nav-local phabricator-side-menu', 206 215 'id' => $local_id, 207 216 ), 208 - self::renderSingleView($this->menu)); 217 + self::renderSingleView($this->menu->setID($this->getMenuID())) 218 + ); 209 219 } 210 220 211 221 $crumbs = null;
+1
webroot/rsrc/css/aphront/form-view.css
··· 196 196 } 197 197 198 198 .aphront-form-control-image .default-image { 199 + display: inline; 199 200 width: 12px; 200 201 } 201 202
+41
webroot/rsrc/css/application/conpherence/header-pane.css
··· 1 + /** 2 + * @provides conpherence-header-pane-css 3 + */ 4 + 5 + .conpherence-header-pane { 6 + border-bottom: 1px solid #ccc; 7 + background: #fff; 8 + height: 50px; 9 + width: 100%; 10 + } 11 + 12 + .conpherence-header-pane .edit { 13 + position: relative; 14 + float: right; 15 + margin: 16px 16px 0px 0px; 16 + } 17 + 18 + .conpherence-header-pane .header-image { 19 + position: absolute; 20 + height: 50px; 21 + width: 50px; 22 + } 23 + 24 + .conpherence-header-pane .title { 25 + position: relative; 26 + font-size: 16px; 27 + left: 62px; 28 + top: 6px; 29 + max-width: 80%; 30 + overflow-x: scroll; 31 + } 32 + 33 + .conpherence-header-pane .subtitle { 34 + position: relative; 35 + left: 62px; 36 + top: 6px; 37 + color: #bfbfbf; 38 + max-width: 80%; 39 + } 40 + 41 +
+121
webroot/rsrc/css/application/conpherence/menu.css
··· 1 + /** 2 + * @provides conpherence-menu-css 3 + */ 4 + 5 + .no-conpherences-menu-item { 6 + color: #ffffff; 7 + border-top: solid 1px #3B3D3E; 8 + padding: 20px 0px 20px 66px; 9 + } 10 + 11 + .conpherence-menu .phabricator-nav-column-background, 12 + .conpherence-menu .phabricator-nav-local { 13 + width: 320px; 14 + } 15 + .conpherence-menu .phabricator-nav-local { 16 + top: 44px; 17 + bottom: 0px; 18 + } 19 + 20 + .conpherence-menu .phabricator-nav-drag { 21 + left: 320px; 22 + } 23 + 24 + .device-desktop .conpherence-menu .phabricator-nav-content { 25 + margin-left: 320px !important; 26 + } 27 + 28 + .conpherence-menu .phabricator-menu-view { 29 + overflow-x: hidden; 30 + overflow-y: scroll; 31 + margin-bottom: 0; 32 + } 33 + 34 + .conpherence-menu .conpherence-menu-item-view { 35 + display: block; 36 + height: 70px; 37 + width: 100%; 38 + overflow: hidden; 39 + position: relative; 40 + text-decoration: none; 41 + border-top: solid 1px #3B3D3E; 42 + border-bottom: solid 1px #1C1F21; 43 + border-right: 0; 44 + border-left: 2px solid transparent; 45 + } 46 + 47 + .conpherence-menu .conpherence-selected { 48 + background: rgba(0, 0, 0, .6); 49 + border-left: 2px solid #66CCFF; 50 + } 51 + 52 + .conpherence-menu .conpherence-menu-item-view:hover { 53 + background-image: url('/rsrc/image/texture/dark-menu-hover.png'); 54 + } 55 + 56 + .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-image { 57 + top: 6px; 58 + left: 6px; 59 + display: block; 60 + position: absolute; 61 + width: 50px; 62 + height: 50px; 63 + border: 4px solid rgb(29, 32, 34); 64 + border-radius: 2px; 65 + } 66 + 67 + .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-title { 68 + display: block; 69 + margin-top: 12px; 70 + margin-left: 70px; 71 + text-align: left; 72 + font-weight: bold; 73 + font-size: 12px; 74 + color: #ffffff; 75 + text-shadow: 0px 1px 1px #000000; 76 + } 77 + 78 + .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-subtitle { 79 + display: block; 80 + color: #bfbfbf; 81 + font-size: 11px; 82 + margin-top: 2px; 83 + margin-left: 70px; 84 + font-style: italic; 85 + } 86 + 87 + .conpherence-menu .conpherence-menu-item-view 88 + .conpherence-menu-item-message-text { 89 + display: block; 90 + color: #66CCFF; 91 + font-size: 12px; 92 + margin-top: 4px; 93 + margin-left: 70px; 94 + } 95 + 96 + .conpherence-menu .conpherence-menu-item-view 97 + .conpherence-menu-item-unread-count { 98 + position: absolute; 99 + left: 48px; 100 + top: 3px; 101 + background: #f00; 102 + border-radius: 10px; 103 + color: white; 104 + font-weight: bold; 105 + padding: 1px 6px 2px; 106 + border: 1px solid #a00; 107 + font-size: 12px; 108 + } 109 + 110 + .conpherence-menu .hide-unread-count .conpherence-menu-item-unread-count, 111 + .conpherence-menu .conpherence-selected .conpherence-menu-item-unread-count { 112 + display: none; 113 + } 114 + 115 + .conpherence-menu .conpherence-menu-item-view .conpherence-menu-item-date { 116 + position: absolute; 117 + top: 10px; 118 + right: 12px; 119 + color: white; 120 + font-size: 12px; 121 + }
+95
webroot/rsrc/css/application/conpherence/message-pane.css
··· 1 + /** 2 + * @provides conpherence-message-pane-css 3 + */ 4 + .conpherence-message-pane { 5 + position: fixed; 6 + left: 320px; 7 + top: 94px; 8 + min-width: 320px; 9 + width: auto; 10 + height: 100%; 11 + margin: 0px 320px 0px 0px; 12 + } 13 + 14 + .conpherence-message-pane .conpherence-messages { 15 + position: fixed; 16 + left: 320px; 17 + right: 321px; 18 + top: 94px; 19 + bottom: 225px; 20 + overflow-y: scroll; 21 + } 22 + 23 + .conpherence-message-pane .phabricator-form-view { 24 + border-width: 0; 25 + background: none; 26 + height: 224px; 27 + padding: 0; 28 + position: fixed; 29 + bottom: 0; 30 + left: 320px; 31 + right: 321px; 32 + } 33 + 34 + .device-tablet 35 + .conpherence-message-pane .phabricator-form-view, 36 + .device-tablet 37 + .conpherence-message-pane .conpherence-messages, 38 + .device-tablet 39 + .conpherence-message-pane { 40 + left: 0px; 41 + } 42 + 43 + .conpherence-message-pane .aphront-form-input { 44 + margin: 0; 45 + width: 100%; 46 + } 47 + 48 + .conpherence-message-pane .phabricator-transaction-view { 49 + margin: 1em 1.25em 1em 1.25em; 50 + display: block; 51 + } 52 + 53 + .conpherence-message-pane .phabricator-transaction-detail { 54 + border-width: 0; 55 + } 56 + 57 + .conpherence-message-pane .phabricator-transaction-header { 58 + background: none; 59 + } 60 + 61 + .conpherence-message-pane .phabricator-transaction-info { 62 + background: none; 63 + } 64 + 65 + .conpherence-message-pane .phabricator-transaction-content { 66 + background: none; 67 + } 68 + 69 + .conpherence-message-pane .conpherence-edited .phabricator-transaction-content { 70 + font-size: 12px; 71 + padding: 0em 1em 0em 1em; 72 + margin-top: -4px; 73 + } 74 + 75 + .conpherence-message-pane .aphront-form-control { 76 + padding: 0; 77 + } 78 + 79 + .conpherence-message-pane .aphront-form-control-submit { 80 + margin-right: 8px; 81 + } 82 + .conpherence-message-pane .remarkup-assist-bar { 83 + border-width: 1px 0 0; 84 + border-color: #CCC; 85 + } 86 + 87 + .conpherence-message-pane .remarkup-assist-textarea { 88 + border-width: 1px 0 1px 0; 89 + border-color: #CCC; 90 + } 91 + 92 + .conpherence-message-pane .remarkup-assist-textarea:focus { 93 + outline: none; 94 + } 95 +
+7
webroot/rsrc/css/application/conpherence/update.css
··· 1 + /** 2 + * @provides conpherence-update-css 3 + */ 4 + 5 + .phabricator-standard-page-body .aphront-dialog-view { 6 + margin: 20px auto 0px auto; 7 + }
+19
webroot/rsrc/css/application/conpherence/widget-pane.css
··· 1 + /** 2 + * @provides conpherence-widget-pane-css 3 + */ 4 + 5 + .conpherence-widget-pane { 6 + position: fixed; 7 + right: 0px; 8 + top: 94px; 9 + width: 320px; 10 + height: 100%; 11 + border-width: 0 0 0 1px; 12 + border-color: #CCC; 13 + border-style: solid; 14 + } 15 + 16 + .conpherence-widget-pane .aphront-form-input { 17 + margin: 0; 18 + width: 100%; 19 + }
+20
webroot/rsrc/js/application/conpherence/behavior-init.js
··· 1 + /** 2 + * @provides javelin-behavior-conpherence-init 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-stratcom 6 + */ 7 + JX.behavior('conpherence-init', function(config) { 8 + // select the current message 9 + var selectedConpherence = false; 10 + if (config.selected_conpherence_id) { 11 + var selected = JX.$(config.selected_conpherence_id); 12 + JX.Stratcom.invoke( 13 + 'conpherence-initial-selected', 14 + null, 15 + { selected : selected } 16 + ); 17 + selectedConpherence = true; 18 + } 19 + 20 + });
+96
webroot/rsrc/js/application/conpherence/behavior-menu.js
··· 1 + /** 2 + * @provides javelin-behavior-conpherence-menu 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-workflow 6 + * javelin-util 7 + * javelin-stratcom 8 + * javelin-uri 9 + */ 10 + 11 + JX.behavior('conpherence-menu', function(config) { 12 + 13 + function onresponse(context, response) { 14 + var header = JX.$H(response.header); 15 + var messages = JX.$H(response.messages); 16 + var form = JX.$H(response.form); 17 + var widgets = JX.$H(response.widgets); 18 + var headerRoot = JX.$(config.header); 19 + var messagesRoot = JX.$(config.messages); 20 + var formRoot = JX.$(config.form_pane); 21 + var widgetsRoot = JX.$(config.widgets_pane); 22 + JX.DOM.setContent(headerRoot, header); 23 + JX.DOM.setContent(messagesRoot, messages); 24 + messagesRoot.scrollTop = messagesRoot.scrollHeight; 25 + JX.DOM.setContent(formRoot, form); 26 + JX.DOM.setContent(widgetsRoot, widgets); 27 + 28 + for (var i = 0; i < context.parentNode.childNodes.length; i++) { 29 + var current = context.parentNode.childNodes[i]; 30 + if (current.id == context.id) { 31 + JX.DOM.alterClass(current, 'conpherence-selected', true); 32 + JX.DOM.alterClass(current, 'hide-unread-count', true); 33 + } else { 34 + JX.DOM.alterClass(current, 'conpherence-selected', false); 35 + } 36 + } 37 + 38 + // TODO - update the browser URI T2086 39 + 40 + JX.Stratcom.invoke( 41 + 'conpherence-selected-loaded', 42 + null, 43 + {} 44 + ); 45 + } 46 + 47 + JX.Stratcom.listen( 48 + 'click', 49 + 'conpherence-menu-click', 50 + function(e) { 51 + e.kill(); 52 + var selected = e.getNode(['conpherence-menu-click']); 53 + if (config.fancy_ajax) { 54 + JX.Stratcom.invoke( 55 + 'conpherence-selected', 56 + null, 57 + { selected : selected } 58 + ); 59 + } else { 60 + var data = JX.Stratcom.getData(selected); 61 + var uri = new JX.URI(config.base_uri + data.id + '/'); 62 + uri.go(); 63 + } 64 + } 65 + ); 66 + 67 + JX.Stratcom.listen( 68 + 'conpherence-initial-selected', 69 + null, 70 + function(e) { 71 + var selected = e.getData().selected; 72 + e.kill(); 73 + JX.Stratcom.invoke( 74 + 'conpherence-selected', 75 + null, 76 + { selected : selected } 77 + ); 78 + } 79 + ); 80 + 81 + JX.Stratcom.listen( 82 + 'conpherence-selected', 83 + null, 84 + function(e) { 85 + 86 + var selected = e.getData().selected; 87 + var data = JX.Stratcom.getData(selected); 88 + 89 + var uri = config.base_uri + 'view/' + data.id + '/'; 90 + new JX.Workflow(uri, {}) 91 + .setHandler(JX.bind(null, onresponse, selected)) 92 + .start(); 93 + } 94 + ); 95 + 96 + });
+26
webroot/rsrc/js/application/conpherence/behavior-widget-pane.js
··· 1 + /** 2 + * @provides javelin-behavior-conpherence-widget-pane 3 + * @requires javelin-behavior 4 + * javelin-dom 5 + * javelin-stratcom 6 + */ 7 + 8 + JX.behavior('conpherence-widget-pane', function(config) { 9 + 10 + JX.Stratcom.listen( 11 + 'click', 12 + 'conpherence-change-widget', 13 + function(e) { 14 + e.kill(); 15 + var data = e.getNodeData('conpherence-change-widget'); 16 + for (var widget in config.widgetRegistery) { 17 + if (widget == data.widget) { 18 + JX.$(widget).style.display = 'block'; 19 + } else { 20 + JX.$(widget).style.display = 'none'; 21 + } 22 + } 23 + } 24 + ); 25 + 26 + });