1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-09-20 01:08:50 +02:00

Allow affiliations to carry project ownership information; transform profile

images correctly

Summary:
This is sort of doing two things at once:

  - Add an "isOwner" flag to Project Affiliation to lay the groundwork for T237.
  - Rename the "QuickCreate" workflow to "Create" and funnel all creation
through it.
  - Reorganize the image transformation stuff and use it to correctly
crop/resize uploaded images.

Test Plan:
Created and edited projects and affailiations. Uploaded project, user, and
profile photos. Verified existing thumbnailing in Maniphest still works
properly.

Reviewed By: cadamo
Reviewers: cadamo, aran, jungejason, tuomaspelkonen
CC: aran, epriestley, cadamo
Differential Revision: 529
This commit is contained in:
epriestley 2011-06-26 08:37:47 -07:00
parent 63436ad74a
commit e0e6ec9117
19 changed files with 266 additions and 114 deletions

View file

@ -0,0 +1,2 @@
ALTER TABLE phabricator_project.project_affiliation
ADD isOwner bool NOT NULL;

View file

@ -354,6 +354,7 @@ phutil_register_library_map(array(
'PhabricatorIRCMessage' => 'infrastructure/daemon/irc/message',
'PhabricatorIRCObjectNameHandler' => 'infrastructure/daemon/irc/handler/objectname',
'PhabricatorIRCProtocolHandler' => 'infrastructure/daemon/irc/handler/protocol',
'PhabricatorImageTransformer' => 'applications/files/transform',
'PhabricatorJavelinLinter' => 'infrastructure/lint/linter/javelin',
'PhabricatorLintEngine' => 'infrastructure/lint/engine',
'PhabricatorLiskDAO' => 'applications/base/storage/lisk',
@ -428,12 +429,12 @@ phutil_register_library_map(array(
'PhabricatorProjectAffiliation' => 'applications/project/storage/affiliation',
'PhabricatorProjectAffiliationEditController' => 'applications/project/controller/editaffiliation',
'PhabricatorProjectController' => 'applications/project/controller/base',
'PhabricatorProjectCreateController' => 'applications/project/controller/create',
'PhabricatorProjectDAO' => 'applications/project/storage/base',
'PhabricatorProjectListController' => 'applications/project/controller/list',
'PhabricatorProjectProfile' => 'applications/project/storage/profile',
'PhabricatorProjectProfileController' => 'applications/project/controller/profile',
'PhabricatorProjectProfileEditController' => 'applications/project/controller/profileedit',
'PhabricatorProjectQuickCreateController' => 'applications/project/controller/quickcreate',
'PhabricatorProjectStatus' => 'applications/project/constants/status',
'PhabricatorProjectTransactionSearch' => 'applications/project/transactions/search',
'PhabricatorRedirectController' => 'applications/base/controller/redirect',
@ -900,12 +901,12 @@ phutil_register_library_map(array(
'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
'PhabricatorProjectAffiliationEditController' => 'PhabricatorProjectController',
'PhabricatorProjectController' => 'PhabricatorController',
'PhabricatorProjectCreateController' => 'PhabricatorProjectController',
'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',
'PhabricatorProjectListController' => 'PhabricatorProjectController',
'PhabricatorProjectProfile' => 'PhabricatorProjectDAO',
'PhabricatorProjectProfileController' => 'PhabricatorProjectController',
'PhabricatorProjectProfileEditController' => 'PhabricatorProjectController',
'PhabricatorProjectQuickCreateController' => 'PhabricatorProjectController',
'PhabricatorRedirectController' => 'PhabricatorController',
'PhabricatorRemarkupRuleDifferential' => 'PhabricatorRemarkupRuleObjectName',
'PhabricatorRemarkupRuleDiffusion' => 'PhutilRemarkupRule',

View file

@ -198,12 +198,12 @@ class AphrontDefaultApplicationConfiguration
'/project/' => array(
'$' => 'PhabricatorProjectListController',
'edit/(?:(?P<id>\d+)/)?$' => 'PhabricatorProjectProfileEditController',
'edit/(?P<id>\d+)/$' => 'PhabricatorProjectProfileEditController',
'view/(?P<id>\d+)/(?:(?P<page>\w+)/)?$'
=> 'PhabricatorProjectProfileController',
'affiliation/(?P<id>\d+)/$'
=> 'PhabricatorProjectAffiliationEditController',
'quickcreate/$' => 'PhabricatorProjectQuickCreateController',
'create/$' => 'PhabricatorProjectCreateController',
),
'/r(?P<callsign>[A-Z]+)(?P<commit>[a-z0-9]+)$'

View file

@ -56,9 +56,6 @@ class PhabricatorFileTransformController extends PhabricatorFileController {
case 'thumb-60x45':
$xformed_file = $this->executeThumbTransform($file, 60, 45);
break;
case 'profile-50x50':
$xformed_file = $this->executeProfile50x50Transform($file);
break;
default:
return new Aphront400Response();
}
@ -118,61 +115,9 @@ class PhabricatorFileTransformController extends PhabricatorFileController {
PhabricatorFileURI::getViewURIForPHID($xform->getTransformedPHID()));
}
private function executeProfile50x50Transform(PhabricatorFile $file) {
$data = $file->loadFileData();
$jpeg = $this->crudelyScaleTo($data, 50, 50);
return PhabricatorFile::newFromFileData($jpeg, array(
'name' => 'profile-'.$file->getName(),
));
}
private function executeThumbTransform(PhabricatorFile $file, $x, $y) {
$data = $file->loadFileData();
$jpeg = $this->crudelyScaleTo($data, $x, $y);
return PhabricatorFile::newFromFileData($jpeg, array(
'name' => 'thumb-'.$file->getName(),
));
}
/**
* Very crudely scale an image up or down to an exact size.
*/
private function crudelyScaleTo($data, $dx, $dy) {
$src = imagecreatefromstring($data);
$x = imagesx($src);
$y = imagesy($src);
$scale = min($x / $dx, $y / $dy);
$dst = imagecreatetruecolor($dx, $dy);
imagecopyresampled(
$dst,
$src,
0, 0,
0, 0,
$dx, $dy,
$scale * $dx, $scale * $dy);
$img = null;
if (function_exists('imagejpeg')) {
ob_start();
imagejpeg($dst);
$img = ob_get_clean();
} else if (function_exists('imagepng')) {
ob_start();
imagepng($dst);
$img = ob_get_clean();
} else if (function_exists('imagegif')) {
ob_start();
imagegif($dst);
$img = ob_get_clean();
} else {
throw new Exception("No image generation functions exist!");
}
return $img;
$xformer = new PhabricatorImageTransformer();
return $xformer->executeThumbTransform($file, $x, $y);
}
}

View file

@ -12,6 +12,7 @@ phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/files/controller/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/files/storage/transformed');
phutil_require_module('phabricator', 'applications/files/transform');
phutil_require_module('phabricator', 'applications/files/uri');
phutil_require_module('phutil', 'utils');

View file

@ -0,0 +1,125 @@
<?php
/*
* Copyright 2011 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
final class PhabricatorImageTransformer {
public function executeThumbTransform(
PhabricatorFile $file,
$x,
$y) {
$data = $file->loadFileData();
$image = $this->crudelyScaleTo($data, $x, $y);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'thumb-'.$file->getName(),
));
}
public function executeProfileTransform(
PhabricatorFile $file,
$x,
$min_y,
$max_y) {
$data = $file->loadFileData();
$image = $this->crudelyCropTo($data, $x, $min_y, $max_y);
return PhabricatorFile::newFromFileData(
$image,
array(
'name' => 'profile-'.$file->getName(),
));
}
private function crudelyCropTo($data, $x, $min_y, $max_y) {
$img = imagecreatefromstring($data);
$sx = imagesx($img);
$sy = imagesy($img);
$scaled_y = ($x / $sx) * $sy;
if ($scaled_y > $max_y) {
// This image is very tall and thin.
$scaled_y = $max_y;
} else if ($scaled_y < $min_y) {
// This image is very short and wide.
$scaled_y = $min_y;
}
$img = $this->applyScaleTo(
$img,
$x,
$scaled_y);
return $this->saveImageDataInAnyFormat($img);
}
/**
* Very crudely scale an image up or down to an exact size.
*/
private function crudelyScaleTo($data, $dx, $dy) {
$src = imagecreatefromstring($data);
$dst = $this->applyScaleTo($src, $dx, $dy);
return $this->saveImageDataInAnyFormat($dst);
}
private function applyScaleTo($src, $dx, $dy) {
$x = imagesx($src);
$y = imagesy($src);
$scale = min($x / $dx, $y / $dy);
$dst = imagecreatetruecolor($dx, $dy);
imagecopyresampled(
$dst,
$src,
0, 0,
0, 0,
$dx, $dy,
$scale * $dx, $scale * $dy);
return $dst;
}
private function saveImageDataInAnyFormat($data) {
$img = null;
if (function_exists('imagejpeg')) {
ob_start();
imagejpeg($data);
$img = ob_get_clean();
} else if (function_exists('imagepng')) {
ob_start();
imagepng($data);
$img = ob_get_clean();
} else if (function_exists('imagegif')) {
ob_start();
imagegif($data);
$img = ob_get_clean();
} else {
throw new Exception("No image generation functions exist!");
}
return $img;
}
}

View file

@ -0,0 +1,12 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_source('PhabricatorImageTransformer.php');

View file

@ -252,13 +252,15 @@ class ManiphestTaskEditController extends ManiphestController {
javelin_render_tag(
'a',
array(
'href' => '/project/quickcreate/',
'href' => '/project/create/',
'mustcapture' => true,
'sigil' => 'project-create',
),
'Create New Project'))
->setDatasource('/typeahead/common/projects/'));
require_celerity_resource('aphront-error-view-css');
Javelin::initBehavior('maniphest-project-create', array(
'tokenizerID' => $project_tokenizer_id,
));

View file

@ -43,7 +43,13 @@ class PhabricatorPeopleProfileEditController
$file = PhabricatorFile::newFromPHPUpload($_FILES['image']);
$okay = $file->isTransformableImage();
if ($okay) {
$profile->setProfileImagePHID($file->getPHID());
$xformer = new PhabricatorImageTransformer();
$xformed = $xformer->executeProfileTransform(
$file,
$width = 280,
$min_height = 140,
$max_height = 420);
$profile->setProfileImagePHID($xformed->getPHID());
} else {
$errors[] =
'Only valid image files (jpg, jpeg, png or gif) '.

View file

@ -8,6 +8,7 @@
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/files/transform');
phutil_require_module('phabricator', 'applications/people/controller/base');
phutil_require_module('phabricator', 'applications/people/storage/profile');
phutil_require_module('phabricator', 'view/form/base');
@ -21,4 +22,4 @@ phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorPeopleProfileEditController.php');
phutil_require_source('PhabricatorPeopleProfileEditController.php');

View file

@ -119,9 +119,14 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController {
if ($err != UPLOAD_ERR_NO_FILE) {
$file = PhabricatorFile::newFromPHPUpload($_FILES['profile']);
$okay = $file->isTransformableImage();
if ($okay) {
$user->setProfileImagePHID($file->getPHID());
$xformer = new PhabricatorImageTransformer();
$xformed = $xformer->executeProfileTransform(
$file,
$width = 50,
$min_height = 50,
$max_height = 50);
$user->setProfileImagePHID($xformed->getPHID());
} else {
$errors[] =
'Only valid image files (jpg, jpeg, png or gif) '.
@ -339,8 +344,7 @@ class PhabricatorUserSettingsController extends PhabricatorPeopleController {
->appendChild(
id(new AphrontFormFileControl())
->setLabel('Change Image')
->setName('profile')
->setCaption('Upload a 50x50px image.'))
->setName('profile'))
->appendChild(
id(new AphrontFormMarkupControl())
->setValue('<hr />'))

View file

@ -12,6 +12,7 @@ phutil_require_module('phabricator', 'aphront/response/dialog');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/auth/oauth/provider/base');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/files/transform');
phutil_require_module('phabricator', 'applications/files/uri');
phutil_require_module('phabricator', 'applications/people/controller/base');
phutil_require_module('phabricator', 'applications/people/storage/user');

View file

@ -16,7 +16,7 @@
* limitations under the License.
*/
class PhabricatorProjectQuickCreateController
class PhabricatorProjectCreateController
extends PhabricatorProjectController {
@ -28,13 +28,14 @@ class PhabricatorProjectQuickCreateController
$project = new PhabricatorProject();
$project->setAuthorPHID($user->getPHID());
$profile = new PhabricatorProjectProfile();
$options = PhabricatorProjectStatus::getStatusMap();
$e_name = true;
$errors = array();
if ($request->isFormPost()) {
$project->setName($request->getStr('name'));
$project->setStatus(PhabricatorProjectStatus::ONGOING);
$profile->setBlurb($request->getStr('blurb'));
if (!strlen($project->getName())) {
@ -49,11 +50,23 @@ class PhabricatorProjectQuickCreateController
$profile->setProjectPHID($project->getPHID());
$profile->save();
return id(new AphrontAjaxResponse())
->setContent(array(
'phid' => $project->getPHID(),
'name' => $project->getName(),
));
id(new PhabricatorProjectAffiliation())
->setUserPHID($user->getPHID())
->setProjectPHID($project->getPHID())
->setRole('Owner')
->setIsOwner(true)
->save();
if ($request->isAjax()) {
return id(new AphrontAjaxResponse())
->setContent(array(
'phid' => $project->getPHID(),
'name' => $project->getName(),
));
} else {
return id(new AphrontRedirectResponse())
->setURI('/project/view/'.$project->getID().'/');
}
}
}
@ -61,11 +74,17 @@ class PhabricatorProjectQuickCreateController
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setWidth(AphrontErrorView::WIDTH_DIALOG);
$error_view->setErrors($errors);
}
$form = id(new AphrontFormLayoutView())
if ($request->isAjax()) {
$form = new AphrontFormLayoutView();
} else {
$form = new AphrontFormView();
$form->setUser($user);
}
$form
->appendChild(
id(new AphrontFormTextControl())
->setLabel('Name')
@ -79,15 +98,44 @@ class PhabricatorProjectQuickCreateController
->setHeight(AphrontFormTextAreaControl::HEIGHT_VERY_SHORT)
->setValue($profile->getBlurb()));
$dialog = id(new AphrontDialogView())
->setUser($user)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle('Create a New Project')
->appendChild($error_view)
->appendChild($form)
->addSubmitButton('Create Project')
->addCancelButton('/project/');
if ($request->isAjax()) {
return id(new AphrontDialogResponse())->setDialog($dialog);
if ($error_view) {
$error_view->setWidth(AphrontErrorView::WIDTH_DIALOG);
}
$dialog = id(new AphrontDialogView())
->setUser($user)
->setWidth(AphrontDialogView::WIDTH_FORM)
->setTitle('Create a New Project')
->appendChild($error_view)
->appendChild($form)
->addSubmitButton('Create Project')
->addCancelButton('/project/');
return id(new AphrontDialogResponse())->setDialog($dialog);
} else {
$form
->appendChild(
id(new AphrontFormSubmitControl())
->setValue('Create')
->addCancelButton('/project/'));
$panel = new AphrontPanelView();
$panel
->setWidth(AphrontPanelView::WIDTH_FORM)
->setHeader('Create a New Project')
->appendChild($form);
return $this->buildStandardPageResponse(
array(
$error_view,
$panel,
),
array(
'title' => 'Create new Project',
));
}
}
}

View file

@ -8,17 +8,22 @@
phutil_require_module('phabricator', 'aphront/response/ajax');
phutil_require_module('phabricator', 'aphront/response/dialog');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/project/constants/status');
phutil_require_module('phabricator', 'applications/project/controller/base');
phutil_require_module('phabricator', 'applications/project/storage/affiliation');
phutil_require_module('phabricator', 'applications/project/storage/profile');
phutil_require_module('phabricator', 'applications/project/storage/project');
phutil_require_module('phabricator', 'view/dialog');
phutil_require_module('phabricator', 'view/form/base');
phutil_require_module('phabricator', 'view/form/control/submit');
phutil_require_module('phabricator', 'view/form/control/text');
phutil_require_module('phabricator', 'view/form/control/textarea');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/form/layout');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorProjectQuickCreateController.php');
phutil_require_source('PhabricatorProjectCreateController.php');

View file

@ -52,7 +52,12 @@ class PhabricatorProjectAffiliationEditController
if (!strlen($affiliation->getRole())) {
if ($affiliation->getID()) {
$affiliation->delete();
if ($affiliation->getIsOwner()) {
$affiliation->setRole('Owner');
$affiliation->save();
} else {
$affiliation->delete();
}
}
} else {
$affiliation->save();

View file

@ -121,7 +121,7 @@ class PhabricatorProjectListController
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader('Project');
$panel->setCreateButton('Create New Project', '/project/edit/');
$panel->setCreateButton('Create New Project', '/project/create/');
return $this->buildStandardPageResponse(
$panel,

View file

@ -19,10 +19,8 @@
class PhabricatorProjectProfileEditController
extends PhabricatorProjectController {
private $id;
public function willProcessRequest(array $data) {
$this->id = idx($data, 'id');
$this->id = $data['id'];
}
public function processRequest() {
@ -30,16 +28,11 @@ class PhabricatorProjectProfileEditController
$request = $this->getRequest();
$user = $request->getUser();
if ($this->id) {
$project = id(new PhabricatorProject())->load($this->id);
if (!$project) {
return new Aphront404Response();
}
$profile = $project->loadProfile();
} else {
$project = new PhabricatorProject();
$project->setAuthorPHID($user->getPHID());
$project = id(new PhabricatorProject())->load($this->id);
if (!$project) {
return new Aphront404Response();
}
$profile = $project->loadProfile();
if (empty($profile)) {
$profile = new PhabricatorProjectProfile();
@ -67,7 +60,13 @@ class PhabricatorProjectProfileEditController
$file = PhabricatorFile::newFromPHPUpload($_FILES['image']);
$okay = $file->isTransformableImage();
if ($okay) {
$profile->setProfileImagePHID($file->getPHID());
$xformer = new PhabricatorImageTransformer();
$xformed = $xformer->executeProfileTransform(
$file,
$width = 280,
$min_height = 140,
$max_height = 420);
$profile->setProfileImagePHID($xformed->getPHID());
} else {
$errors[] =
'Only valid image files (jpg, jpeg, png or gif) '.
@ -92,15 +91,9 @@ class PhabricatorProjectProfileEditController
$error_view->setErrors($errors);
}
if ($project->getID()) {
$header_name = 'Edit Project';
$title = 'Edit Project';
$action = '/project/edit/'.$project->getID().'/';
} else {
$header_name = 'Create Project';
$title = 'Create Project';
$action = '/project/edit/';
}
$header_name = 'Edit Project';
$title = 'Edit Project';
$action = '/project/edit/'.$project->getID().'/';
$form = new AphrontFormView();
$form
@ -127,8 +120,7 @@ class PhabricatorProjectProfileEditController
->appendChild(
id(new AphrontFormFileControl())
->setLabel('Change Image')
->setName('image')
->setCaption('Upload a 280px-wide image.'))
->setName('image'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/project/view/'.$project->getID().'/')

View file

@ -9,6 +9,7 @@
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'aphront/response/redirect');
phutil_require_module('phabricator', 'applications/files/storage/file');
phutil_require_module('phabricator', 'applications/files/transform');
phutil_require_module('phabricator', 'applications/project/constants/status');
phutil_require_module('phabricator', 'applications/project/controller/base');
phutil_require_module('phabricator', 'applications/project/storage/profile');

View file

@ -21,7 +21,8 @@ class PhabricatorProjectAffiliation extends PhabricatorProjectDAO {
protected $projectPHID;
protected $userPHID;
protected $role;
protected $status;
protected $status = '';
protected $isOwner = 0;
public static function loadAllForProjectPHIDs($phids) {
if (!$phids) {