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

Make new logo and wordmark more reasonably configurable by human users

Summary: Fixes T11437. Provides a normal form for configuring this, instead of weird "look up the PHID and adjust things in the database" stuff.

Test Plan:
{F1753651}

{F1753652}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T11437

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

+200 -140
+2 -2
src/__phutil_library_map__.php
··· 2268 2268 'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php', 2269 2269 'PhabricatorCustomFieldStorageQuery' => 'infrastructure/customfield/query/PhabricatorCustomFieldStorageQuery.php', 2270 2270 'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php', 2271 - 'PhabricatorCustomHeaderConfigType' => 'applications/config/custom/PhabricatorCustomHeaderConfigType.php', 2271 + 'PhabricatorCustomLogoConfigType' => 'applications/config/custom/PhabricatorCustomLogoConfigType.php', 2272 2272 'PhabricatorDaemon' => 'infrastructure/daemon/PhabricatorDaemon.php', 2273 2273 'PhabricatorDaemonBulkJobController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobController.php', 2274 2274 'PhabricatorDaemonBulkJobListController' => 'applications/daemon/controller/PhabricatorDaemonBulkJobListController.php', ··· 7014 7014 'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO', 7015 7015 'PhabricatorCustomFieldStorageQuery' => 'Phobject', 7016 7016 'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage', 7017 - 'PhabricatorCustomHeaderConfigType' => 'PhabricatorConfigOptionType', 7017 + 'PhabricatorCustomLogoConfigType' => 'PhabricatorConfigOptionType', 7018 7018 'PhabricatorDaemon' => 'PhutilDaemon', 7019 7019 'PhabricatorDaemonBulkJobController' => 'PhabricatorDaemonController', 7020 7020 'PhabricatorDaemonBulkJobListController' => 'PhabricatorDaemonBulkJobController',
+4
src/applications/config/check/PhabricatorExtraConfigSetupCheck.php
··· 328 328 329 329 'metamta.re-prefix' => $global_settings_reason, 330 330 'metamta.vary-subjects' => $global_settings_reason, 331 + 332 + 'ui.custom-header' => pht( 333 + 'This option has been replaced with `ui.logo`, which provides more '. 334 + 'flexible configuration options.'), 331 335 ); 332 336 333 337 return $ancient_config;
+25 -22
src/applications/config/controller/PhabricatorConfigEditController.php
··· 98 98 } 99 99 } 100 100 101 - $form = new AphrontFormView(); 101 + $form = id(new AphrontFormView()) 102 + ->setEncType('multipart/form-data'); 102 103 103 104 $error_view = null; 104 105 if ($errors) { ··· 144 145 } 145 146 146 147 if ($option->getHidden() || $option->getLocked()) { 147 - $control = null; 148 + $controls = array(); 148 149 } else { 149 - $control = $this->renderControl( 150 + $controls = $this->renderControls( 150 151 $option, 151 152 $display_value, 152 153 $e_value); ··· 201 202 } 202 203 } 203 204 204 - $form 205 - ->appendChild($control); 206 - 205 + foreach ($controls as $control) { 206 + $form->appendControl($control); 207 + } 207 208 208 209 if (!$option->getLocked()) { 209 210 $form->appendChild( ··· 279 280 $e_value = null; 280 281 $errors = array(); 281 282 282 - $value = $request->getStr('value'); 283 - if (!strlen($value)) { 284 - $value = null; 285 - 286 - $xaction->setNewValue( 287 - array( 288 - 'deleted' => true, 289 - 'value' => null, 290 - )); 291 - 292 - return array($e_value, $errors, $value, $xaction); 293 - } 294 - 295 283 if ($option->isCustomType()) { 296 284 $info = $option->getCustomObject()->readRequest($option, $request); 297 285 list($e_value, $errors, $set_value, $value) = $info; 298 286 } else { 287 + $value = $request->getStr('value'); 288 + if (!strlen($value)) { 289 + $value = null; 290 + 291 + $xaction->setNewValue( 292 + array( 293 + 'deleted' => true, 294 + 'value' => null, 295 + )); 296 + 297 + return array($e_value, $errors, $value, $xaction); 298 + } 299 + 299 300 $type = $option->getType(); 300 301 $set_value = null; 301 302 ··· 415 416 } 416 417 } 417 418 418 - private function renderControl( 419 + private function renderControls( 419 420 PhabricatorConfigOption $option, 420 421 $display_value, 421 422 $e_value) { 422 423 423 424 if ($option->isCustomType()) { 424 - $control = $option->getCustomObject()->renderControl( 425 + $controls = $option->getCustomObject()->renderControls( 425 426 $option, 426 427 $display_value, 427 428 $e_value); ··· 487 488 ->setError($e_value) 488 489 ->setValue($display_value) 489 490 ->setName('value'); 491 + 492 + $controls = array($control); 490 493 } 491 494 492 - return $control; 495 + return $controls; 493 496 } 494 497 495 498 private function renderExamples(PhabricatorConfigOption $option) {
+10
src/applications/config/custom/PhabricatorConfigOptionType.php
··· 31 31 32 32 } 33 33 34 + public function renderControls( 35 + PhabricatorConfigOption $option, 36 + $display_value, 37 + $e_value) { 38 + 39 + $control = $this->renderControl($option, $display_value, $e_value); 40 + 41 + return array($control); 42 + } 43 + 34 44 public function renderControl( 35 45 PhabricatorConfigOption $option, 36 46 $display_value,
-48
src/applications/config/custom/PhabricatorCustomHeaderConfigType.php
··· 1 - <?php 2 - 3 - final class PhabricatorCustomHeaderConfigType 4 - extends PhabricatorConfigOptionType { 5 - 6 - public function validateOption(PhabricatorConfigOption $option, $value) { 7 - if (phid_get_type($value) != PhabricatorFileFilePHIDType::TYPECONST) { 8 - throw new Exception( 9 - pht( 10 - '%s is not a valid file PHID.', 11 - $value)); 12 - } 13 - 14 - $file = id(new PhabricatorFileQuery()) 15 - ->setViewer(PhabricatorUser::getOmnipotentUser()) 16 - ->withPHIDs(array($value)) 17 - ->executeOne(); 18 - if (!$file) { 19 - throw new Exception( 20 - pht( 21 - '%s is not a valid file PHID.', 22 - $value)); 23 - } 24 - 25 - $most_open_policy = PhabricatorPolicies::getMostOpenPolicy(); 26 - if ($file->getViewPolicy() != $most_open_policy) { 27 - throw new Exception( 28 - pht( 29 - 'Specified file %s has policy "%s" but should have policy "%s".', 30 - $value, 31 - $file->getViewPolicy(), 32 - $most_open_policy)); 33 - } 34 - 35 - if (!$file->isViewableImage()) { 36 - throw new Exception( 37 - pht( 38 - 'Specified file %s is not a viewable image.', 39 - $value)); 40 - } 41 - } 42 - 43 - public static function getExampleConfig() { 44 - $config = 'PHID-FILE-abcd1234abcd1234abcd'; 45 - return $config; 46 - } 47 - 48 - }
+118
src/applications/config/custom/PhabricatorCustomLogoConfigType.php
··· 1 + <?php 2 + 3 + final class PhabricatorCustomLogoConfigType 4 + extends PhabricatorConfigOptionType { 5 + 6 + public static function getLogoImagePHID() { 7 + $logo = PhabricatorEnv::getEnvConfig('ui.logo'); 8 + return idx($logo, 'logoImagePHID'); 9 + } 10 + 11 + public static function getLogoWordmark() { 12 + $logo = PhabricatorEnv::getEnvConfig('ui.logo'); 13 + return idx($logo, 'wordmarkText'); 14 + } 15 + 16 + public function validateOption(PhabricatorConfigOption $option, $value) { 17 + if (!is_array($value)) { 18 + throw new Exception( 19 + pht( 20 + 'Logo configuration is not valid: value must be a dictionary.')); 21 + } 22 + 23 + PhutilTypeSpec::checkMap( 24 + $value, 25 + array( 26 + 'logoImagePHID' => 'optional string|null', 27 + 'wordmarkText' => 'optional string|null', 28 + )); 29 + } 30 + 31 + public function readRequest( 32 + PhabricatorConfigOption $option, 33 + AphrontRequest $request) { 34 + 35 + $viewer = $request->getViewer(); 36 + $view_policy = PhabricatorPolicies::POLICY_PUBLIC; 37 + 38 + if ($request->getBool('removeLogo')) { 39 + $logo_image_phid = null; 40 + } else if ($request->getFileExists('logoImage')) { 41 + $logo_image = PhabricatorFile::newFromPHPUpload( 42 + idx($_FILES, 'logoImage'), 43 + array( 44 + 'name' => 'logo', 45 + 'authorPHID' => $viewer->getPHID(), 46 + 'viewPolicy' => $view_policy, 47 + 'canCDN' => true, 48 + 'isExplicitUpload' => true, 49 + )); 50 + $logo_image_phid = $logo_image->getPHID(); 51 + } else { 52 + $logo_image_phid = self::getLogoImagePHID(); 53 + } 54 + 55 + $wordmark_text = $request->getStr('wordmarkText'); 56 + 57 + $value = array( 58 + 'logoImagePHID' => $logo_image_phid, 59 + 'wordmarkText' => $wordmark_text, 60 + ); 61 + 62 + $errors = array(); 63 + $e_value = null; 64 + 65 + try { 66 + $this->validateOption($option, $value); 67 + } catch (Exception $ex) { 68 + $e_value = pht('Invalid'); 69 + $errors[] = $ex->getMessage(); 70 + $value = array(); 71 + } 72 + 73 + return array($e_value, $errors, $value, phutil_json_encode($value)); 74 + } 75 + 76 + public function renderControls( 77 + PhabricatorConfigOption $option, 78 + $display_value, 79 + $e_value) { 80 + 81 + try { 82 + $value = phutil_json_decode($display_value); 83 + } catch (Exception $ex) { 84 + $value = array(); 85 + } 86 + 87 + $logo_image_phid = idx($value, 'logoImagePHID'); 88 + $wordmark_text = idx($value, 'wordmarkText'); 89 + 90 + $controls = array(); 91 + 92 + // TODO: This should be a PHUIFormFileControl, but that currently only 93 + // works in "workflow" forms. It isn't trivial to convert this form into 94 + // a workflow form, nor is it trivial to make the newer control work 95 + // in non-workflow forms. 96 + $controls[] = id(new AphrontFormFileControl()) 97 + ->setName('logoImage') 98 + ->setLabel(pht('Logo Image')); 99 + 100 + if ($logo_image_phid) { 101 + $controls[] = id(new AphrontFormCheckboxControl()) 102 + ->addCheckbox( 103 + 'removeLogo', 104 + 1, 105 + pht('Remove Custom Logo')); 106 + } 107 + 108 + $controls[] = id(new AphrontFormTextControl()) 109 + ->setName('wordmarkText') 110 + ->setLabel(pht('Wordmark')) 111 + ->setPlaceholder(pht('Phabricator')) 112 + ->setValue($wordmark_text); 113 + 114 + return $controls; 115 + } 116 + 117 + 118 + }
+13 -36
src/applications/config/option/PhabricatorUIConfigOptions.php
··· 20 20 } 21 21 22 22 public function getOptions() { 23 - $custom_header_example = 24 - PhabricatorCustomHeaderConfigType::getExampleConfig(); 25 - $experimental_link = 'https://secure.phabricator.com/T4214'; 26 23 $options = array( 27 24 'blindigo' => 'blindigo', 28 25 'red' => 'red', ··· 48 45 ] 49 46 EOJSON; 50 47 48 + $logo_type = 'custom:PhabricatorCustomLogoConfigType'; 49 + 51 50 return array( 52 51 $this->newOption('ui.header-color', 'enum', 'blindigo') 53 52 ->setDescription( 54 53 pht('Sets the default color scheme of Phabricator.')) 55 54 ->setEnumOptions($options), 55 + $this->newOption('ui.logo', $logo_type, array()) 56 + ->setSummary( 57 + pht('Customize the logo and wordmark text in the header.')) 58 + ->setDescription( 59 + pht( 60 + "Customize the logo image and text which appears in the main ". 61 + "site header:\n\n". 62 + " - **Logo Image**: Upload a new 80 x 80px image to replace the ". 63 + "Phabricator logo in the site header.\n\n". 64 + " - **Wordmark**: Choose new text to display next to the logo. ". 65 + "By default, the header displays //Phabricator//.\n\n")), 56 66 $this->newOption('ui.footer-items', 'list<wild>', array()) 57 67 ->setSummary( 58 68 pht( ··· 69 79 " omit this if you just want a piece of text, like a copyright ". 70 80 " notice.")) 71 81 ->addExample($example, pht('Basic Example')), 72 - $this->newOption('ui.custom-wordmark', 'string', array()) 73 - ->setSummary( 74 - pht( 75 - 'Customize the text next to the logo.')) 76 - ->setDescription( 77 - pht( 78 - "Allows you to change the text (Phabricator by default) ". 79 - "next to the Phabricator logo.\n\n")), 80 - $this->newOption( 81 - 'ui.custom-header', 82 - 'custom:PhabricatorCustomHeaderConfigType', 83 - null) 84 - ->setSummary( 85 - pht('Customize the Phabricator logo.')) 86 - ->setDescription( 87 - pht('You can customize the Phabricator logo by specifying the '. 88 - 'phid for a viewable image you have uploaded to Phabricator '. 89 - 'via the [[ /file/ | Files application]]. This image should '. 90 - 'be:'."\n". 91 - ' - 80px X 80px; while not enforced, images with these '. 92 - 'dimensions will look best across devices.'."\n". 93 - ' - have view policy public if [[ '. 94 - '/config/edit/policy.allow-public | `policy.allow-public`]] '. 95 - 'is true and otherwise view policy user; mismatches in these '. 96 - 'policy settings will result in a broken logo for some users.'. 97 - "\n\n". 98 - 'You should restart Phabricator after updating this value '. 99 - 'to see this change take effect.'. 100 - "\n\n". 101 - 'As this feature is experimental, please read [[ %s | T4214 ]] '. 102 - 'for up to date information.', 103 - $experimental_link)) 104 - ->addExample($custom_header_example, pht('Valid Config')), 105 82 ); 106 83 } 107 84
+28 -32
src/view/page/menu/PhabricatorMainMenuView.php
··· 297 297 } 298 298 299 299 private function renderPhabricatorLogo() { 300 - $style_logo = null; 301 - $logo = null; 302 - $custom_header = PhabricatorEnv::getEnvConfig('ui.custom-header'); 303 - $custom_wordmark = PhabricatorEnv::getEnvConfig('ui.custom-wordmark'); 300 + $custom_header = PhabricatorCustomLogoConfigType::getLogoImagePHID(); 301 + 302 + $logo_style = array(); 304 303 if ($custom_header) { 305 304 $cache = PhabricatorCaches::getImmutableCache(); 306 - $cache_key_logo = 'ui.custom-header.logo-phid.v1.'.$custom_header; 305 + $cache_key_logo = 'ui.custom-header.logo-phid.v3.'.$custom_header; 306 + 307 307 $logo_uri = $cache->getKey($cache_key_logo); 308 308 if (!$logo_uri) { 309 309 $file = id(new PhabricatorFileQuery()) ··· 315 315 $cache->setKey($cache_key_logo, $logo_uri); 316 316 } 317 317 } 318 - if ($logo_uri) { 319 - $style_logo = 320 - 'background-size: 40px 40px; '. 321 - 'background-position: 0px 0px; '. 322 - 'background-image: url('.$logo_uri.');'; 323 - } 324 318 325 - $logo = phutil_tag( 326 - 'span', 327 - array( 328 - 'class' => 'phabricator-main-menu-logo', 329 - 'style' => $style_logo, 330 - ), 331 - ''); 319 + $logo_style[] = 'background-size: 40px 40px;'; 320 + $logo_style[] = 'background-position: 0 0;'; 321 + $logo_style[] = 'background-image: url('.$logo_uri.')'; 332 322 } 333 323 334 - if (!$logo) { 335 - $logo = phutil_tag( 336 - 'span', 337 - array( 338 - 'class' => 'phabricator-wordmark', 339 - ), 340 - (($custom_wordmark) ? $custom_wordmark : pht('Phabricator'))); 324 + $logo_node = phutil_tag( 325 + 'span', 326 + array( 327 + 'class' => 'phabricator-main-menu-eye', 328 + 'style' => implode(' ', $logo_style), 329 + )); 330 + 331 + 332 + $wordmark_text = PhabricatorCustomLogoConfigType::getLogoWordmark(); 333 + if (!strlen($wordmark_text)) { 334 + $wordmark_text = pht('Phabricator'); 341 335 } 342 336 337 + $wordmark_node = phutil_tag( 338 + 'span', 339 + array( 340 + 'class' => 'phabricator-wordmark', 341 + ), 342 + $wordmark_text); 343 + 343 344 return phutil_tag( 344 345 'a', 345 346 array( ··· 353 354 'aural' => true, 354 355 ), 355 356 pht('Home')), 356 - phutil_tag( 357 - 'span', 358 - array( 359 - 'class' => 'phabricator-main-menu-eye', 360 - ), 361 - ''), 362 - $logo, 357 + $logo_node, 358 + $wordmark_node, 363 359 )); 364 360 } 365 361