@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.

Show Aphlict connection status in notification menu

Summary:
Fixes T5373. Ref T5281. Several changes:

- The `marshallExceptions` thing is useful if JS throws an exception when invoked from Flash, so set it. The resulting exceptions are a little odd (not escaped correctly, e.g.) but way better than nothing.
- Put connection status in the notification menu.
- When the connection fails, try to provide contextual help where we can.

Test Plan: {F169493}

Reviewers: chad, joshuaspence

Reviewed By: joshuaspence

Subscribers: epriestley

Maniphest Tasks: T5281, T5373

Differential Revision: https://secure.phabricator.com/D9700

+217 -40
+33 -24
resources/celerity/map.php
··· 7 7 return array( 8 8 'names' => 9 9 array( 10 - 'core.pkg.css' => '22e4fc33', 11 - 'core.pkg.js' => 'f5ba2408', 10 + 'core.pkg.css' => 'f9c94804', 11 + 'core.pkg.js' => '8c184823', 12 12 'darkconsole.pkg.js' => 'df001cab', 13 13 'differential.pkg.css' => '4a93db37', 14 14 'differential.pkg.js' => 'd1443567', ··· 37 37 'rsrc/css/aphront/typeahead.css' => 'a989b5b3', 38 38 'rsrc/css/application/auth/auth.css' => '1e655982', 39 39 'rsrc/css/application/base/main-menu-view.css' => 'aceca0e9', 40 - 'rsrc/css/application/base/notification-menu.css' => 'cbff1b94', 40 + 'rsrc/css/application/base/notification-menu.css' => '8ae4a008', 41 41 'rsrc/css/application/base/phabricator-application-launch-view.css' => '8b7e271d', 42 42 'rsrc/css/application/base/standard-page-view.css' => '517cdfb1', 43 43 'rsrc/css/application/chatlog/chatlog.css' => '852140ff', ··· 346 346 'rsrc/image/texture/table_header.png' => '5c433037', 347 347 'rsrc/image/texture/table_header_hover.png' => '038ec3b9', 348 348 'rsrc/image/texture/table_header_tall.png' => 'd56b434f', 349 - 'rsrc/js/application/aphlict/Aphlict.js' => 'da12704d', 350 - 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => '862ea0fe', 349 + 'rsrc/js/application/aphlict/Aphlict.js' => '4a07e8e3', 350 + 'rsrc/js/application/aphlict/behavior-aphlict-dropdown.js' => 'f51afce0', 351 351 'rsrc/js/application/aphlict/behavior-aphlict-listen.js' => 'a826c925', 352 + 'rsrc/js/application/aphlict/behavior-aphlict-status.js' => '58f7803f', 352 353 'rsrc/js/application/auth/behavior-persona-login.js' => '9414ff18', 353 354 'rsrc/js/application/config/behavior-reorder-fields.js' => '14a827de', 354 355 'rsrc/js/application/conpherence/behavior-menu.js' => 'f0a41b9f', ··· 490 491 'rsrc/js/phuix/PHUIXActionListView.js' => 'b5c256b8', 491 492 'rsrc/js/phuix/PHUIXActionView.js' => '6e8cefa4', 492 493 'rsrc/js/phuix/PHUIXDropdownMenu.js' => 'bd4c8dca', 493 - 'rsrc/swf/aphlict.swf' => 'ef64606d', 494 + 'rsrc/swf/aphlict.swf' => 'e5a24c72', 494 495 ), 495 496 'symbols' => 496 497 array( ··· 539 540 'herald-rule-editor' => '6c9e6fb8', 540 541 'herald-test-css' => '778b008e', 541 542 'inline-comment-summary-css' => '8cfd34e8', 542 - 'javelin-aphlict' => 'da12704d', 543 + 'javelin-aphlict' => '4a07e8e3', 543 544 'javelin-behavior' => '8a3ed18b', 544 - 'javelin-behavior-aphlict-dropdown' => '862ea0fe', 545 + 'javelin-behavior-aphlict-dropdown' => 'f51afce0', 545 546 'javelin-behavior-aphlict-listen' => 'a826c925', 547 + 'javelin-behavior-aphlict-status' => '58f7803f', 546 548 'javelin-behavior-aphront-basic-tokenizer' => 'b3a4b884', 547 549 'javelin-behavior-aphront-crop' => 'fa0f4fc2', 548 550 'javelin-behavior-aphront-drag-and-drop-textarea' => '92eb531d', ··· 725 727 'phabricator-nav-view-css' => '9283c2df', 726 728 'phabricator-notification' => '0c6946e7', 727 729 'phabricator-notification-css' => 'ef2c9b34', 728 - 'phabricator-notification-menu-css' => 'cbff1b94', 730 + 'phabricator-notification-menu-css' => '8ae4a008', 729 731 'phabricator-object-selector-css' => '029a133d', 730 732 'phabricator-phtize' => 'd254d646', 731 733 'phabricator-prefab' => '41ed7994', ··· 1190 1192 1 => 'javelin-dom', 1191 1193 2 => 'javelin-reactor-dom', 1192 1194 ), 1195 + '4a07e8e3' => 1196 + array( 1197 + 0 => 'javelin-install', 1198 + 1 => 'javelin-util', 1199 + ), 1193 1200 '4d94d9c3' => 1194 1201 array( 1195 1202 0 => 'javelin-behavior', ··· 1235 1242 2 => 'javelin-vector', 1236 1243 3 => 'javelin-dom', 1237 1244 ), 1245 + '58f7803f' => 1246 + array( 1247 + 0 => 'javelin-behavior', 1248 + 1 => 'javelin-aphlict', 1249 + 2 => 'phabricator-phtize', 1250 + 3 => 'javelin-dom', 1251 + ), 1238 1252 '59b251eb' => 1239 1253 array( 1240 1254 0 => 'javelin-behavior', ··· 1476 1490 2 => 'javelin-util', 1477 1491 3 => 'javelin-workflow', 1478 1492 4 => 'javelin-stratcom', 1479 - ), 1480 - '862ea0fe' => 1481 - array( 1482 - 0 => 'javelin-behavior', 1483 - 1 => 'javelin-request', 1484 - 2 => 'javelin-stratcom', 1485 - 3 => 'javelin-vector', 1486 - 4 => 'javelin-dom', 1487 - 5 => 'javelin-uri', 1488 - 6 => 'javelin-behavior-device', 1489 1493 ), 1490 1494 '880fa5ac' => 1491 1495 array( ··· 1921 1925 1 => 'javelin-util', 1922 1926 2 => 'javelin-stratcom', 1923 1927 ), 1924 - 'da12704d' => 1925 - array( 1926 - 0 => 'javelin-install', 1927 - 1 => 'javelin-util', 1928 - ), 1929 1928 'dd7e8ef5' => 1930 1929 array( 1931 1930 0 => 'javelin-behavior', ··· 2037 2036 4 => 'phuix-action-list-view', 2038 2037 5 => 'phuix-action-view', 2039 2038 6 => 'javelin-workflow', 2039 + ), 2040 + 'f51afce0' => 2041 + array( 2042 + 0 => 'javelin-behavior', 2043 + 1 => 'javelin-request', 2044 + 2 => 'javelin-stratcom', 2045 + 3 => 'javelin-vector', 2046 + 4 => 'javelin-dom', 2047 + 5 => 'javelin-uri', 2048 + 6 => 'javelin-behavior-device', 2040 2049 ), 2041 2050 'f588412e' => 2042 2051 array(
+2
src/__phutil_library_map__.php
··· 1803 1803 'PhabricatorNotificationPanelController' => 'applications/notification/controller/PhabricatorNotificationPanelController.php', 1804 1804 'PhabricatorNotificationQuery' => 'applications/notification/PhabricatorNotificationQuery.php', 1805 1805 'PhabricatorNotificationStatusController' => 'applications/notification/controller/PhabricatorNotificationStatusController.php', 1806 + 'PhabricatorNotificationStatusView' => 'applications/notification/view/PhabricatorNotificationStatusView.php', 1806 1807 'PhabricatorNotificationTestController' => 'applications/notification/controller/PhabricatorNotificationTestController.php', 1807 1808 'PhabricatorOAuthClientAuthorization' => 'applications/oauthserver/storage/PhabricatorOAuthClientAuthorization.php', 1808 1809 'PhabricatorOAuthClientAuthorizationQuery' => 'applications/oauthserver/query/PhabricatorOAuthClientAuthorizationQuery.php', ··· 4631 4632 'PhabricatorNotificationPanelController' => 'PhabricatorNotificationController', 4632 4633 'PhabricatorNotificationQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 4633 4634 'PhabricatorNotificationStatusController' => 'PhabricatorNotificationController', 4635 + 'PhabricatorNotificationStatusView' => 'AphrontTagView', 4634 4636 'PhabricatorNotificationTestController' => 'PhabricatorNotificationController', 4635 4637 'PhabricatorOAuthClientAuthorization' => 4636 4638 array(
+24 -9
src/applications/notification/controller/PhabricatorNotificationPanelController.php
··· 25 25 pht('You have no notifications.')); 26 26 } 27 27 28 + $notifications_link = phutil_tag( 29 + 'a', 30 + array( 31 + 'href' => '/notification/', 32 + ), 33 + pht('Notifications')); 34 + 35 + $connection_status = new PhabricatorNotificationStatusView(); 36 + 37 + $header = phutil_tag( 38 + 'div', 39 + array( 40 + 'class' => 'phabricator-notification-header', 41 + ), 42 + array( 43 + $connection_status, 44 + $notifications_link, 45 + )); 46 + 28 47 $content = hsprintf( 29 - '<div class="phabricator-notification-header">%s %s</div>'. 30 48 '%s'. 31 - '<div class="phabricator-notification-view-all">%s</div>', 32 - phutil_tag( 33 - 'a', 34 - array( 35 - 'href' => '/notification/', 36 - ), 37 - pht('Notifications')), 49 + '%s'. 50 + '<div class="phabricator-notification-view-all">%s %s %s</div>', 51 + $header, 52 + $content, 38 53 javelin_tag( 39 54 'a', 40 55 array( ··· 43 58 'class' => 'phabricator-notification-clear-all' 44 59 ), 45 60 pht('Mark All Read')), 46 - $content, 61 + " \xC2\xB7 ", 47 62 phutil_tag( 48 63 'a', 49 64 array(
+34
src/applications/notification/view/PhabricatorNotificationStatusView.php
··· 1 + <?php 2 + 3 + final class PhabricatorNotificationStatusView extends AphrontTagView { 4 + 5 + protected function getTagAttributes() { 6 + if (!$this->getID()) { 7 + $this->setID(celerity_generate_unique_node_id()); 8 + } 9 + 10 + Javelin::initBehavior( 11 + 'aphlict-status', 12 + array( 13 + 'nodeID' => $this->getID(), 14 + 'pht' => array( 15 + 'setup' => pht('Setting Up Client'), 16 + 'start' => pht('Starting Client'), 17 + 'ready' => pht('Ready to Connect'), 18 + 'connecting' => pht('Connecting...'), 19 + 'connected' => pht('Connected'), 20 + 'error' => pht('Connection Error'), 21 + 'client' => pht('Connected Locally'), 22 + 23 + 'error.flash.xdomain' => pht( 24 + 'Unable to connect to Flash Policy Server. Check that the '. 25 + 'notification server is running and port 843 is not firewalled.'), 26 + ), 27 + )); 28 + 29 + return array( 30 + 'class' => 'aphlict-connection-status', 31 + ); 32 + } 33 + 34 + }
-4
support/aphlict/client/src/Aphlict.as
··· 42 42 this.externalInvoke('log', message); 43 43 } 44 44 45 - final protected function setStatus(status:String):void { 46 - this.externalInvoke('status', {type: status}); 47 - } 48 - 49 45 } 50 46 51 47 }
+5
support/aphlict/client/src/AphlictClient.as
··· 41 41 UncaughtErrorEvent.UNCAUGHT_ERROR, 42 42 this.uncaughtErrorHandler); 43 43 44 + ExternalInterface.marshallExceptions = true; 44 45 ExternalInterface.addCallback('connect', this.externalConnect); 45 46 46 47 this.setStatus('ready'); ··· 148 149 public function receiveMessage(msg:Object):void { 149 150 this.log('Received message.'); 150 151 this.externalInvoke('receive', msg); 152 + } 153 + 154 + public function setStatus(status:String, code:String = null):void { 155 + this.externalInvoke('status', {type: status, code: code}); 151 156 } 152 157 153 158 }
+29 -2
support/aphlict/client/src/AphlictMaster.as
··· 48 48 private var socket:Socket; 49 49 private var readBuffer:ByteArray; 50 50 51 + private var status:String; 52 + private var statusCode:String; 53 + 51 54 52 55 public function AphlictMaster(server:String, port:Number) { 53 56 super(); ··· 75 78 if (!this.clients[client]) { 76 79 this.log('Registering client: ' + client); 77 80 this.clients[client] = new Date().getTime(); 81 + 82 + this.send.send(client, 'setStatus', this.status, this.statusCode); 78 83 } 79 84 } 80 85 ··· 106 111 } 107 112 108 113 private function connectToServer():void { 114 + this.setStatusOnClients('connecting'); 115 + 109 116 var socket:Socket = new Socket(); 110 117 111 118 socket.addEventListener(Event.CONNECT, didConnectSocket); ··· 124 131 } 125 132 126 133 private function didConnectSocket(event:Event):void { 127 - this.externalInvoke('connected'); 134 + this.setStatusOnClients('connected'); 128 135 129 136 // Send subscriptions 130 137 var phids = new Array(); ··· 146 153 } 147 154 148 155 private function didSecurityErrorSocket(event:SecurityErrorEvent):void { 149 - this.externalInvoke('error', event.text); 156 + var text = event.text; 157 + 158 + // This is really gross but there doesn't seem to be anything else 159 + // on the object which gives us an error code. 160 + if (text.match(/^Error #2048/)) { 161 + this.setStatusOnClients('error', 'error.flash.xdomain'); 162 + } 163 + 164 + this.error(text); 150 165 } 151 166 152 167 public function subscribe(client:String, phids:Array):void { ··· 277 292 } while (true); 278 293 } catch (err:Error) { 279 294 this.error(err); 295 + } 296 + } 297 + 298 + private function setStatusOnClients( 299 + status:String, 300 + code:String = null):void { 301 + 302 + this.status = status; 303 + this.statusCode = code; 304 + 305 + for (var client:String in this.clients) { 306 + this.send.send(client, 'setStatus', status, code); 280 307 } 281 308 } 282 309
+14
webroot/rsrc/css/application/base/notification-menu.css
··· 97 97 padding: 8px; 98 98 font-size: 12px; 99 99 } 100 + 101 + .phabricator-notification-menu .aphlict-connection-status { 102 + float: right; 103 + font-weight: normal; 104 + color: {$lightgreytext}; 105 + } 106 + 107 + .aphlict-connection-status .aphlict-connection-status-connected { 108 + color: {$green}; 109 + } 110 + 111 + .aphlict-connection-status .aphlict-connection-status-error { 112 + color: {$red}; 113 + }
+23 -1
webroot/rsrc/js/application/aphlict/Aphlict.js
··· 28 28 construct: function(id, server, port, subscriptions) { 29 29 if (__DEV__) { 30 30 if (JX.Aphlict._instance) { 31 - JX.$E('Aphlict object is a singleton!'); 31 + JX.$E('Aphlict object is a singleton.'); 32 32 } 33 33 } 34 34 ··· 36 36 this._server = server; 37 37 this._port = port; 38 38 this._subscriptions = subscriptions; 39 + this._setStatus('setup'); 39 40 40 41 JX.Aphlict._instance = this; 41 42 }, 42 43 44 + events: ['didChangeStatus'], 45 + 43 46 members: { 44 47 _id: null, 45 48 _server: null, 46 49 _port: null, 47 50 _subscriptions: null, 51 + _status: null, 52 + _statusCode: null, 48 53 49 54 start: function(node, uri) { 55 + this._setStatus('start'); 56 + 50 57 // NOTE: This is grotesque, but seems to work everywhere. 51 58 node.innerHTML = 52 59 '<object classid="clsid:d27cdb6e-ae6d-11cf-96b8-444553540000">' + ··· 72 79 this._server, 73 80 this._port, 74 81 this._subscriptions); 82 + }, 83 + 84 + getStatus: function() { 85 + return this._status; 86 + }, 87 + 88 + getStatusCode: function() { 89 + return this._statusCode; 90 + }, 91 + 92 + _setStatus: function(status, code) { 93 + this._status = status; 94 + this._statusCode = code || null; 95 + this.invoke('didChangeStatus'); 75 96 } 76 97 77 98 }, ··· 98 119 } 99 120 100 121 if (type == 'status') { 122 + client._setStatus(message.type, message.code); 101 123 switch (message.type) { 102 124 case 'ready': 103 125 client._didStartFlash();
+6
webroot/rsrc/js/application/aphlict/behavior-aphlict-dropdown.js
··· 73 73 return; 74 74 } 75 75 76 + if (!e.getNode('notification')) { 77 + // User clicked somewhere in the dead area of the menu, like the header 78 + // or footer. 79 + return; 80 + } 81 + 76 82 // If the user clicked a notification (but missed a link) and it has a 77 83 // primary URI, go there. 78 84 var href = e.getNodeData('notification').href;
+47
webroot/rsrc/js/application/aphlict/behavior-aphlict-status.js
··· 1 + /** 2 + * @provides javelin-behavior-aphlict-status 3 + * @requires javelin-behavior 4 + * javelin-aphlict 5 + * phabricator-phtize 6 + * javelin-dom 7 + * @javelin 8 + */ 9 + 10 + JX.behavior('aphlict-status', function(config) { 11 + var pht = JX.phtize(config.pht); 12 + 13 + function update() { 14 + var client = JX.Aphlict.getInstance(); 15 + if (!client) { 16 + return; 17 + } 18 + 19 + var node; 20 + try { 21 + node = JX.$(config.nodeID); 22 + } catch (ignored) { 23 + return; 24 + } 25 + 26 + var tip = null; 27 + var status = client.getStatus(); 28 + 29 + if (status == 'error') { 30 + tip = pht(client.getStatusCode()); 31 + } 32 + 33 + var status_node = JX.$N( 34 + 'span', 35 + { 36 + className: 'aphlict-connection-status-' + status, 37 + sigil: tip ? 'has-tooltip' : null, 38 + meta: tip ? {tip: tip, align: 'S', size: 300} : {} 39 + }, 40 + pht(status)); 41 + 42 + JX.DOM.setContent(node, status_node); 43 + } 44 + 45 + JX.Aphlict.listen('didChangeStatus', update); 46 + update(); 47 + });
webroot/rsrc/swf/aphlict.swf

This is a binary file and will not be displayed.