@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 365 lines 9.8 kB view raw
1<?php 2 3final class PhabricatorProjectEditPictureController 4 extends PhabricatorProjectController { 5 6 public function handleRequest(AphrontRequest $request) { 7 $viewer = $request->getViewer(); 8 $id = $request->getURIData('id'); 9 10 $project = id(new PhabricatorProjectQuery()) 11 ->setViewer($viewer) 12 ->withIDs(array($id)) 13 ->needImages(true) 14 ->requireCapabilities( 15 array( 16 PhabricatorPolicyCapability::CAN_VIEW, 17 PhabricatorPolicyCapability::CAN_EDIT, 18 )) 19 ->executeOne(); 20 if (!$project) { 21 return new Aphront404Response(); 22 } 23 24 $this->setProject($project); 25 26 $manage_uri = $this->getApplicationURI('manage/'.$project->getID().'/'); 27 28 $supported_formats = PhabricatorFile::getTransformableImageFormats(); 29 if ($supported_formats) { 30 $supported_formats_message = pht('Supported image formats: %s.', 31 implode(', ', $supported_formats)); 32 } else { 33 $supported_formats_message = pht('Server supports no image formats.'); 34 } 35 $e_file = true; 36 $errors = array(); 37 38 // Get the image file transform. 39 $xform = PhabricatorFileTransform::getTransformByKey( 40 PhabricatorFileThumbnailTransform::TRANSFORM_PROFILE); 41 42 // Have an hard-limit to save our resources. 43 $max_image_dimensions = $xform->getMaxTransformDimensions(); 44 $max_image_dimensions_message = pht('Maximum image dimensions: %s pixels.', 45 implode(mb_chr(215), $max_image_dimensions)); 46 47 if ($request->isFormPost()) { 48 $phid = $request->getStr('phid'); 49 $is_default = false; 50 if ($phid == PhabricatorPHIDConstants::PHID_VOID) { 51 $phid = null; 52 $is_default = true; 53 } else if ($phid) { 54 $file = id(new PhabricatorFileQuery()) 55 ->setViewer($viewer) 56 ->withPHIDs(array($phid)) 57 ->executeOne(); 58 } else { 59 if ($request->getFileExists('picture')) { 60 $file = PhabricatorFile::newFromPHPUpload( 61 $_FILES['picture'], 62 array( 63 'authorPHID' => $viewer->getPHID(), 64 'canCDN' => true, 65 )); 66 } else { 67 $e_file = pht('Required'); 68 $errors[] = pht( 69 'You must choose a file when uploading a new project picture.'); 70 } 71 } 72 73 if (!$errors && !$is_default) { 74 if (!$file->isTransformableImage()) { 75 $e_file = pht('Not Supported'); 76 $errors[] = $supported_formats_message; 77 } else { 78 $xformed = $xform->executeTransformExplicit($file); 79 } 80 } 81 82 if (!$errors) { 83 if ($is_default) { 84 $new_value = null; 85 } else { 86 $new_value = $xformed->getPHID(); 87 } 88 89 $xactions = array(); 90 $xactions[] = id(new PhabricatorProjectTransaction()) 91 ->setTransactionType( 92 PhabricatorProjectImageTransaction::TRANSACTIONTYPE) 93 ->setNewValue($new_value); 94 95 $editor = id(new PhabricatorProjectTransactionEditor()) 96 ->setActor($viewer) 97 ->setContentSourceFromRequest($request) 98 ->setContinueOnMissingFields(true) 99 ->setContinueOnNoEffect(true); 100 101 $editor->applyTransactions($project, $xactions); 102 103 return id(new AphrontRedirectResponse())->setURI($manage_uri); 104 } 105 } 106 107 $title = pht('Edit Project Picture'); 108 109 $form = id(new PHUIFormLayoutView()) 110 ->setViewer($viewer); 111 112 $builtin = PhabricatorProjectIconSet::getIconImage( 113 $project->getIcon()); 114 $default_image = PhabricatorFile::loadBuiltin($this->getViewer(), 115 'projects/'.$builtin); 116 117 $images = array(); 118 119 $current = $project->getProfileImagePHID(); 120 $has_current = false; 121 if ($current) { 122 $files = id(new PhabricatorFileQuery()) 123 ->setViewer($viewer) 124 ->withPHIDs(array($current)) 125 ->execute(); 126 if ($files) { 127 $file = head($files); 128 if ($file->isTransformableImage()) { 129 $has_current = true; 130 $images[$current] = array( 131 'uri' => $file->getBestURI(), 132 'tip' => pht('Current Picture'), 133 ); 134 } 135 } 136 } 137 138 $root = dirname(phutil_get_library_root('phabricator')); 139 $root = $root.'/resources/builtin/projects/v3/'; 140 141 $builtins = id(new FileFinder($root)) 142 ->withType('f') 143 ->withFollowSymlinks(true) 144 ->find(); 145 146 foreach ($builtins as $builtin) { 147 $file = PhabricatorFile::loadBuiltin($viewer, 'projects/v3/'.$builtin); 148 $images[$file->getPHID()] = array( 149 'uri' => $file->getBestURI(), 150 'tip' => pht('Builtin Image'), 151 ); 152 } 153 154 $images[PhabricatorPHIDConstants::PHID_VOID] = array( 155 'uri' => $default_image->getBestURI(), 156 'tip' => pht('Default Picture'), 157 ); 158 159 require_celerity_resource('people-profile-css'); 160 Javelin::initBehavior('phabricator-tooltips', array()); 161 162 $buttons = array(); 163 foreach ($images as $phid => $spec) { 164 $button = javelin_tag( 165 'button', 166 array( 167 'class' => 'button-grey profile-image-button', 168 'sigil' => 'has-tooltip', 169 'meta' => array( 170 'tip' => $spec['tip'], 171 'size' => 300, 172 ), 173 ), 174 phutil_tag( 175 'img', 176 array( 177 'height' => 50, 178 'width' => 50, 179 'src' => $spec['uri'], 180 ))); 181 182 $button = array( 183 phutil_tag( 184 'input', 185 array( 186 'type' => 'hidden', 187 'name' => 'phid', 188 'value' => $phid, 189 )), 190 $button, 191 ); 192 193 $button = phabricator_form( 194 $viewer, 195 array( 196 'class' => 'profile-image-form', 197 'method' => 'POST', 198 ), 199 $button); 200 201 $buttons[] = $button; 202 } 203 204 if ($has_current) { 205 $form->appendChild( 206 id(new AphrontFormMarkupControl()) 207 ->setLabel(pht('Current Picture')) 208 ->setValue(array_shift($buttons))); 209 } 210 211 $form->appendChild( 212 id(new AphrontFormMarkupControl()) 213 ->setLabel(pht('Use Picture')) 214 ->setValue( 215 array( 216 $this->renderDefaultForm($project), 217 $buttons, 218 ))); 219 220 $launch_id = celerity_generate_unique_node_id(); 221 $input_id = celerity_generate_unique_node_id(); 222 223 Javelin::initBehavior( 224 'launch-icon-composer', 225 array( 226 'launchID' => $launch_id, 227 'inputID' => $input_id, 228 )); 229 230 $compose_button = javelin_tag( 231 'button', 232 array( 233 'class' => 'button-grey', 234 'id' => $launch_id, 235 'sigil' => 'icon-composer', 236 ), 237 pht('Choose Icon and Color...')); 238 239 $compose_input = javelin_tag( 240 'input', 241 array( 242 'type' => 'hidden', 243 'id' => $input_id, 244 'name' => 'phid', 245 )); 246 247 $compose_form = phabricator_form( 248 $viewer, 249 array( 250 'class' => 'profile-image-form', 251 'method' => 'POST', 252 ), 253 array( 254 $compose_input, 255 $compose_button, 256 )); 257 258 $form->appendChild( 259 id(new AphrontFormMarkupControl()) 260 ->setLabel(pht('Custom')) 261 ->setValue($compose_form)); 262 263 $upload_form = id(new AphrontFormView()) 264 ->setViewer($viewer) 265 ->setEncType('multipart/form-data') 266 ->appendChild( 267 id(new AphrontFormFileControl()) 268 ->setName('picture') 269 ->setLabel(pht('Upload Picture')) 270 ->setError($e_file) 271 ->setCaption($supported_formats_message.' '. 272 $max_image_dimensions_message)) 273 ->appendChild( 274 id(new AphrontFormSubmitControl()) 275 ->addCancelButton($manage_uri) 276 ->setValue(pht('Upload Picture'))); 277 278 $form_box = id(new PHUIObjectBoxView()) 279 ->setHeaderText($title) 280 ->setFormErrors($errors) 281 ->setForm($form); 282 283 $upload_box = id(new PHUIObjectBoxView()) 284 ->setHeaderText(pht('Upload New Picture')) 285 ->setForm($upload_form); 286 287 $nav = $this->newNavigation( 288 $project, 289 PhabricatorProject::ITEM_MANAGE); 290 291 return $this->newPage() 292 ->setTitle($title) 293 ->setNavigation($nav) 294 ->appendChild( 295 array( 296 $form_box, 297 $upload_box, 298 )); 299 } 300 301 private function renderDefaultForm(PhabricatorProject $project) { 302 $viewer = $this->getViewer(); 303 $compose_color = $project->getDisplayIconComposeColor(); 304 $compose_icon = $project->getDisplayIconComposeIcon(); 305 306 $default_builtin = id(new PhabricatorFilesComposeIconBuiltinFile()) 307 ->setColor($compose_color) 308 ->setIcon($compose_icon); 309 310 $file_builtins = PhabricatorFile::loadBuiltins( 311 $viewer, 312 array($default_builtin)); 313 314 $file_builtin = head($file_builtins); 315 316 $default_button = javelin_tag( 317 'button', 318 array( 319 'class' => 'button-grey profile-image-button', 320 'sigil' => 'has-tooltip', 321 'meta' => array( 322 'tip' => pht('Use Icon and Color'), 323 'size' => 300, 324 ), 325 ), 326 phutil_tag( 327 'img', 328 array( 329 'height' => 50, 330 'width' => 50, 331 'src' => $file_builtin->getBestURI(), 332 ))); 333 334 $inputs = array( 335 'projectPHID' => $project->getPHID(), 336 'icon' => $compose_icon, 337 'color' => $compose_color, 338 ); 339 340 foreach ($inputs as $key => $value) { 341 $inputs[$key] = javelin_tag( 342 'input', 343 array( 344 'type' => 'hidden', 345 'name' => $key, 346 'value' => $value, 347 )); 348 } 349 350 $default_form = phabricator_form( 351 $viewer, 352 array( 353 'class' => 'profile-image-form', 354 'method' => 'POST', 355 'action' => '/file/compose/', 356 ), 357 array( 358 $inputs, 359 $default_button, 360 )); 361 362 return $default_form; 363 } 364 365}