@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
at upstream/main 141 lines 3.7 kB view raw
1<?php 2 3final class DarkConsoleCore extends Phobject { 4 5 private $plugins = array(); 6 const STORAGE_VERSION = 1; 7 8 public function __construct() { 9 $this->plugins = id(new PhutilClassMapQuery()) 10 ->setAncestorClass(DarkConsolePlugin::class) 11 ->execute(); 12 13 foreach ($this->plugins as $plugin) { 14 $plugin->setConsoleCore($this); 15 $plugin->didStartup(); 16 } 17 } 18 19 public function getPlugins() { 20 return $this->plugins; 21 } 22 23 public function getKey(AphrontRequest $request) { 24 $plugins = $this->getPlugins(); 25 26 foreach ($plugins as $plugin) { 27 $plugin->setRequest($request); 28 $plugin->willShutdown(); 29 } 30 31 foreach ($plugins as $plugin) { 32 $plugin->didShutdown(); 33 } 34 35 foreach ($plugins as $plugin) { 36 $plugin->setData($plugin->generateData()); 37 } 38 39 $plugins = msort($plugins, 'getOrderKey'); 40 41 $key = Filesystem::readRandomCharacters(24); 42 43 $tabs = array(); 44 $data = array(); 45 foreach ($plugins as $plugin) { 46 $class = get_class($plugin); 47 $tabs[] = array( 48 'class' => $class, 49 'name' => $plugin->getName(), 50 'color' => $plugin->getColor(), 51 ); 52 $data[$class] = $this->sanitizeForJSON($plugin->getData()); 53 } 54 55 $storage = array( 56 'vers' => self::STORAGE_VERSION, 57 'tabs' => $tabs, 58 'data' => $data, 59 'user' => $request->getUser() 60 ? $request->getUser()->getPHID() 61 : null, 62 ); 63 64 $cache = new PhabricatorKeyValueDatabaseCache(); 65 $cache = new PhutilKeyValueCacheProfiler($cache); 66 $cache->setProfiler(PhutilServiceProfiler::getInstance()); 67 68 // This encoding may fail if there are, e.g., database queries which 69 // include binary data. It would be a little cleaner to try to strip these, 70 // but just do something non-broken here if we end up with unrepresentable 71 // data. 72 $json = @json_encode($storage); 73 if (!$json) { 74 $json = '{}'; 75 } 76 77 $cache->setKeys( 78 array( 79 'darkconsole:'.$key => $json, 80 ), 81 $ttl = (60 * 60 * 6)); 82 83 return $key; 84 } 85 86 public function getColor() { 87 foreach ($this->getPlugins() as $plugin) { 88 if ($plugin->getColor()) { 89 return $plugin->getColor(); 90 } 91 } 92 } 93 94 public function render(AphrontRequest $request) { 95 $user = $request->getUser(); 96 $visible = $user->getUserSetting( 97 PhabricatorDarkConsoleVisibleSetting::SETTINGKEY); 98 99 return javelin_tag( 100 'div', 101 array( 102 'id' => 'darkconsole', 103 'class' => 'dark-console', 104 'style' => $visible ? '' : 'display: none;', 105 'data-console-key' => $this->getKey($request), 106 'data-console-color' => $this->getColor(), 107 ), 108 ''); 109 } 110 111 /** 112 * Sometimes, tab data includes binary information (like INSERT queries which 113 * write file data into the database). To successfully JSON encode it, we 114 * need to convert it to UTF-8. 115 */ 116 private function sanitizeForJSON($data) { 117 if (is_object($data)) { 118 return '<object:'.get_class($data).'>'; 119 } else if (is_array($data)) { 120 foreach ($data as $key => $value) { 121 $data[$key] = $this->sanitizeForJSON($value); 122 } 123 return $data; 124 } else if (is_resource($data)) { 125 return '<resource>'; 126 } else { 127 // This is very probably not a string in strict sense 128 $data = phutil_string_cast($data); 129 130 // Truncate huge strings. Since the data doesn't really matter much, 131 // just truncate bytes to avoid PhutilUTF8StringTruncator overhead. 132 $length = strlen($data); 133 $max = 4096; 134 if ($length > $max) { 135 $data = substr($data, 0, $max).'...<'.$length.' bytes>...'; 136 } 137 return phutil_utf8ize($data); 138 } 139 } 140 141}