Project list and profile view modifications
Summary: Added some change on the project's list view, to show information about active tasks, population, etc. Also modified the "profile view", and added a class "PhabricatorProfileView" to render the profile, both on projects and users. Test Plan: play around the project directory :) Reviewers: epriestley ericfrenkiel CC: Differential Revision: 477
This commit is contained in:
@@ -24,10 +24,50 @@ class PhabricatorProjectListController
|
||||
$projects = id(new PhabricatorProject())->loadAllWhere(
|
||||
'1 = 1 ORDER BY id DESC limit 100');
|
||||
|
||||
$author_phids = mpull($projects, 'getAuthorPHID');
|
||||
$handles = id(new PhabricatorObjectHandleData($author_phids))
|
||||
->loadHandles();
|
||||
|
||||
$rows = array();
|
||||
foreach ($projects as $project) {
|
||||
$documents = new PhabricatorProjectTransactionSearch($project->getPHID());
|
||||
// search all open documents by default
|
||||
$documents->setSearchOptions();
|
||||
$documents = $documents->executeSearch();
|
||||
|
||||
$documents_types = igroup($documents,'documentType');
|
||||
$tasks = idx(
|
||||
$documents_types,
|
||||
PhabricatorPHIDConstants::PHID_TYPE_TASK);
|
||||
$tasks_amount = count($tasks);
|
||||
|
||||
// TODO: set up a relationship between the project and the arcanist's
|
||||
// project, to be able get the revisions.
|
||||
$revisions = idx(
|
||||
$documents_types,
|
||||
PhabricatorPHIDConstants::PHID_TYPE_DREV);
|
||||
$revisions_amount = count($revisions);
|
||||
|
||||
$profile = $project->getProfile();
|
||||
$affiliations = $project->loadAffiliations();
|
||||
$population = count($affiliations);
|
||||
|
||||
$status = PhabricatorProjectStatus::getNameForStatus(
|
||||
$project->getStatus());
|
||||
|
||||
$blurb = nonempty(
|
||||
$profile->getBlurb(),
|
||||
'Oops!, nothing is known about this elusive project.');
|
||||
$blurb = $this->textWrap($blurb, $columns = 100);
|
||||
|
||||
$rows[] = array(
|
||||
phutil_escape_html($project->getName()),
|
||||
phutil_escape_html($blurb),
|
||||
$handles[$project->getAuthorPHID()]->renderLink(),
|
||||
phutil_escape_html($population),
|
||||
phutil_escape_html($status),
|
||||
phutil_escape_html($tasks_amount),
|
||||
// phutil_escape_html($revisions_amount),
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
@@ -42,11 +82,23 @@ class PhabricatorProjectListController
|
||||
$table->setHeaders(
|
||||
array(
|
||||
'Project',
|
||||
'Blurb',
|
||||
'Mastermind',
|
||||
'Population',
|
||||
'Status',
|
||||
'Open Tasks',
|
||||
// 'Open Revisions',
|
||||
'',
|
||||
));
|
||||
$table->setColumnClasses(
|
||||
array(
|
||||
'pri',
|
||||
'wide',
|
||||
'',
|
||||
'right',
|
||||
'pri',
|
||||
'right',
|
||||
// 'right',
|
||||
'action',
|
||||
));
|
||||
|
||||
@@ -62,4 +114,17 @@ class PhabricatorProjectListController
|
||||
));
|
||||
}
|
||||
|
||||
private function textWrap($text, $length) {
|
||||
if (strlen($text) <= $length) {
|
||||
return $text;
|
||||
} else {
|
||||
// TODO: perhaps this could be improved, adding the ability to get the
|
||||
// last letter and suppress it, if it is one of [(,:; ,etc.
|
||||
// making "blurb" looks a little bit better. :)
|
||||
$wrapped = wordwrap($text, $length, '__#END#__');
|
||||
$end_position = strpos($wrapped, '__#END#__');
|
||||
$wrapped = substr($text, 0, $end_position).'...';
|
||||
return $wrapped;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,8 +6,12 @@
|
||||
|
||||
|
||||
|
||||
phutil_require_module('phabricator', 'applications/phid/constants');
|
||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||
phutil_require_module('phabricator', 'applications/project/constants/status');
|
||||
phutil_require_module('phabricator', 'applications/project/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/project/storage/project');
|
||||
phutil_require_module('phabricator', 'applications/project/transactions/search');
|
||||
phutil_require_module('phabricator', 'view/control/table');
|
||||
phutil_require_module('phabricator', 'view/layout/panel');
|
||||
|
||||
|
||||
@@ -20,139 +20,168 @@ class PhabricatorProjectProfileController
|
||||
extends PhabricatorProjectController {
|
||||
|
||||
private $id;
|
||||
private $page;
|
||||
|
||||
public function willProcessRequest(array $data) {
|
||||
$this->id = $data['id'];
|
||||
$this->id = idx($data, 'id');
|
||||
$this->page = idx($data, 'page');
|
||||
}
|
||||
|
||||
public function processRequest() {
|
||||
$request = $this->getRequest();
|
||||
$user = $request->getUser();
|
||||
$uri = $request->getRequestURI();
|
||||
|
||||
$project = id(new PhabricatorProject())->load($this->id);
|
||||
if (!$project) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$profile = id(new PhabricatorProjectProfile())->loadOneWhere(
|
||||
'projectPHID = %s',
|
||||
$project->getPHID());
|
||||
$profile = $project->getProfile();
|
||||
if (!$profile) {
|
||||
$profile = new PhabricatorProjectProfile();
|
||||
}
|
||||
|
||||
require_celerity_resource('phabricator-profile-css');
|
||||
|
||||
$src_phid = $profile->getProfileImagePHID();
|
||||
$src = PhabricatorFileURI::getViewURIForPHID($src_phid);
|
||||
if (!$src_phid) {
|
||||
$src_phid = $user->getProfileImagePHID();
|
||||
}
|
||||
$picture = PhabricatorFileURI::getViewURIForPHID($src_phid);
|
||||
|
||||
$picture = phutil_render_tag(
|
||||
'img',
|
||||
array(
|
||||
'class' => 'profile-image',
|
||||
'src' => $src,
|
||||
));
|
||||
$pages = array(
|
||||
/*
|
||||
'<h2>Active Documents</h2>',
|
||||
'tasks' => 'Maniphest Tasks',
|
||||
'revisions' => 'Differential Revisions',
|
||||
'<hr />',
|
||||
'<h2>Workflow</h2>',
|
||||
'goals' => 'Goals',
|
||||
'statistics' => 'Statistics',
|
||||
'<hr />', */
|
||||
'<h2>Information</h2>',
|
||||
'edit' => 'Edit Profile',
|
||||
'affiliation' => 'Edit Affiliation',
|
||||
);
|
||||
|
||||
$links =
|
||||
'<ul class="profile-nav-links">'.
|
||||
'<li><a href="/project/edit/'.$project->getID().'/">'.
|
||||
'Edit Project</a></li>'.
|
||||
'<li><a href="/project/affiliation/'.$project->getID().'/">'.
|
||||
'Edit Affiliation</a></li>'.
|
||||
'</ul>';
|
||||
if (empty($pages[$this->page])) {
|
||||
$this->page = 'action'; // key($pages);
|
||||
}
|
||||
|
||||
$blurb = nonempty(
|
||||
$profile->getBlurb(),
|
||||
'//Nothing is known about this elusive project.//');
|
||||
switch ($this->page) {
|
||||
default:
|
||||
$content = $this->renderBasicInformation($project, $profile);
|
||||
break;
|
||||
}
|
||||
|
||||
$factory = new DifferentialMarkupEngineFactory();
|
||||
$engine = $factory->newDifferentialCommentMarkupEngine();
|
||||
$blurb = $engine->markupText($blurb);
|
||||
|
||||
|
||||
$affiliations = id(new PhabricatorProjectAffiliation())->loadAllWhere(
|
||||
'projectPHID = %s ORDER BY IF(status = "former", 1, 0), dateCreated',
|
||||
$project->getPHID());
|
||||
|
||||
$phids = array_merge(
|
||||
array($project->getAuthorPHID()),
|
||||
mpull($affiliations, 'getUserPHID'));
|
||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||
->loadHandles();
|
||||
|
||||
$affiliated = array();
|
||||
foreach ($affiliations as $affiliation) {
|
||||
$user = $handles[$affiliation->getUserPHID()]->renderLink();
|
||||
$role = phutil_escape_html($affiliation->getRole());
|
||||
|
||||
$status = null;
|
||||
if ($affiliation->getStatus() == 'former') {
|
||||
$role = '<em>Former '.$role.'</em>';
|
||||
$profile = new PhabricatorProfileView();
|
||||
$profile->setProfilePicture($picture);
|
||||
$profile->setProfileNames($project->getName());
|
||||
foreach ($pages as $page => $name) {
|
||||
if (is_integer($page)) {
|
||||
$profile->addProfileItem(
|
||||
phutil_render_tag(
|
||||
'span',
|
||||
array(),
|
||||
$name));
|
||||
} else {
|
||||
$uri->setPath('/project/'.$page.'/'.$project->getID().'/');
|
||||
$profile->addProfileItem(
|
||||
phutil_render_tag(
|
||||
'a',
|
||||
array(
|
||||
'href' => $uri,
|
||||
'class' => ($this->page == $page)
|
||||
? 'phabricator-profile-item-selected'
|
||||
: null,
|
||||
),
|
||||
phutil_escape_html($name)));
|
||||
}
|
||||
|
||||
$affiliated[] = '<li>'.$user.' — '.$role.$status.'</li>';
|
||||
}
|
||||
if ($affiliated) {
|
||||
$affiliated = '<ul>'.implode("\n", $affiliated).'</ul>';
|
||||
} else {
|
||||
$affiliated = '<p><em>No one is affiliated with this project.</em></p>';
|
||||
}
|
||||
|
||||
$timestamp = phabricator_format_timestamp($project->getDateCreated());
|
||||
|
||||
$content =
|
||||
'<div class="phabricator-profile-info-group">
|
||||
<h1 class="phabricator-profile-info-header">Basic Information</h1>
|
||||
<div class="phabricator-profile-info-pane">
|
||||
<table class="phabricator-profile-info-table">
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>'.$handles[$project->getAuthorPHID()]->renderLink().'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created</th>
|
||||
<td>'.$timestamp.'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>PHID</th>
|
||||
<td>'.phutil_escape_html($project->getPHID()).'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Blurb</th>
|
||||
<td>'.$blurb.'</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
$content .=
|
||||
'<div class="phabricator-profile-info-group">
|
||||
<h1 class="phabricator-profile-info-header">Resources</h1>
|
||||
<div class="phabricator-profile-info-pane">'.
|
||||
$affiliated.
|
||||
'</div>
|
||||
</div>';
|
||||
|
||||
|
||||
$profile_markup =
|
||||
'<table class="phabricator-profile-master-layout">
|
||||
<tr>
|
||||
<td class="phabricator-profile-navigation">'.
|
||||
'<h1>'.phutil_escape_html($project->getName()).'</h1>'.
|
||||
'<hr />'.
|
||||
$picture.
|
||||
'<hr />'.
|
||||
$links.
|
||||
'<hr />'.
|
||||
'</td>
|
||||
<td class="phabricator-profile-content">'.
|
||||
$content.
|
||||
'</td>
|
||||
</tr>
|
||||
</table>';
|
||||
$profile->appendChild($content);
|
||||
|
||||
return $this->buildStandardPageResponse(
|
||||
$profile_markup,
|
||||
$profile,
|
||||
array(
|
||||
'title' => $project->getName(),
|
||||
));
|
||||
));
|
||||
}
|
||||
|
||||
private function renderBasicInformation($project, $profile) {
|
||||
$blurb = nonempty(
|
||||
$profile->getBlurb(),
|
||||
'//Nothing is known about this elusive project.//');
|
||||
|
||||
$factory = new DifferentialMarkupEngineFactory();
|
||||
$engine = $factory->newDifferentialCommentMarkupEngine();
|
||||
$blurb = $engine->markupText($blurb);
|
||||
|
||||
$affiliations = $project->loadAffiliations();
|
||||
|
||||
$phids = array_merge(
|
||||
array($project->getAuthorPHID()),
|
||||
mpull($affiliations, 'getUserPHID'));
|
||||
$handles = id(new PhabricatorObjectHandleData($phids))
|
||||
->loadHandles();
|
||||
|
||||
$affiliated = array();
|
||||
foreach ($affiliations as $affiliation) {
|
||||
$user = $handles[$affiliation->getUserPHID()]->renderLink();
|
||||
$role = phutil_escape_html($affiliation->getRole());
|
||||
|
||||
$status = null;
|
||||
if ($affiliation->getStatus() == 'former') {
|
||||
$role = '<em>Former '.$role.'</em>';
|
||||
}
|
||||
|
||||
$affiliated[] = '<li>'.$user.' — '.$role.$status.'</li>';
|
||||
}
|
||||
if ($affiliated) {
|
||||
$affiliated = '<ul>'.implode("\n", $affiliated).'</ul>';
|
||||
} else {
|
||||
$affiliated = '<p><em>No one is affiliated with this project.</em></p>';
|
||||
}
|
||||
|
||||
$timestamp = phabricator_format_timestamp($project->getDateCreated());
|
||||
$status = PhabricatorProjectStatus::getNameForStatus(
|
||||
$project->getStatus());
|
||||
|
||||
$content =
|
||||
'<div class="phabricator-profile-info-group">
|
||||
<h1 class="phabricator-profile-info-header">Basic Information</h1>
|
||||
<div class="phabricator-profile-info-pane">
|
||||
<table class="phabricator-profile-info-table">
|
||||
<tr>
|
||||
<th>Creator</th>
|
||||
<td>'.$handles[$project->getAuthorPHID()]->renderLink().'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Status</th>
|
||||
<td><strong>'.phutil_escape_html($status).'</strong></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Created</th>
|
||||
<td>'.$timestamp.'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>PHID</th>
|
||||
<td>'.phutil_escape_html($project->getPHID()).'</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Blurb</th>
|
||||
<td>'.$blurb.'</td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
</div>';
|
||||
|
||||
$content .=
|
||||
'<div class="phabricator-profile-info-group">
|
||||
<h1 class="phabricator-profile-info-header">Resources</h1>
|
||||
<div class="phabricator-profile-info-pane">'.
|
||||
$affiliated.
|
||||
'</div>
|
||||
</div>';
|
||||
|
||||
return $content;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,11 +10,11 @@ phutil_require_module('phabricator', 'aphront/response/404');
|
||||
phutil_require_module('phabricator', 'applications/differential/parser/markup');
|
||||
phutil_require_module('phabricator', 'applications/files/uri');
|
||||
phutil_require_module('phabricator', 'applications/phid/handle/data');
|
||||
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', 'infrastructure/celerity/api');
|
||||
phutil_require_module('phabricator', 'view/layout/profile');
|
||||
phutil_require_module('phabricator', 'view/utils');
|
||||
|
||||
phutil_require_module('phutil', 'markup');
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
class PhabricatorProjectEditController
|
||||
class PhabricatorProjectProfileEditController
|
||||
extends PhabricatorProjectController {
|
||||
|
||||
private $id;
|
||||
@@ -35,9 +35,7 @@ class PhabricatorProjectEditController
|
||||
if (!$project) {
|
||||
return new Aphront404Response();
|
||||
}
|
||||
$profile = id(new PhabricatorProjectProfile())->loadOneWhere(
|
||||
'projectPHID = %s',
|
||||
$project->getPHID());
|
||||
$profile = $project->getProfile();
|
||||
} else {
|
||||
$project = new PhabricatorProject();
|
||||
$project->setAuthorPHID($user->getPHID());
|
||||
@@ -47,12 +45,13 @@ class PhabricatorProjectEditController
|
||||
$profile = new PhabricatorProjectProfile();
|
||||
}
|
||||
|
||||
$options = PhabricatorProjectStatus::getStatusMap();
|
||||
|
||||
$e_name = true;
|
||||
$errors = array();
|
||||
|
||||
if ($request->isFormPost()) {
|
||||
|
||||
$project->setName($request->getStr('name'));
|
||||
$project->setStatus($request->getStr('status'));
|
||||
$profile->setBlurb($request->getStr('blurb'));
|
||||
|
||||
if (!strlen($project->getName())) {
|
||||
@@ -62,6 +61,21 @@ class PhabricatorProjectEditController
|
||||
$e_name = null;
|
||||
}
|
||||
|
||||
if (!empty($_FILES['image'])) {
|
||||
$err = idx($_FILES['image'], 'error');
|
||||
if ($err != UPLOAD_ERR_NO_FILE) {
|
||||
$file = PhabricatorFile::newFromPHPUpload($_FILES['image']);
|
||||
$okay = $file->isTransformableImage();
|
||||
if ($okay) {
|
||||
$profile->setProfileImagePHID($file->getPHID());
|
||||
} else {
|
||||
$errors[] =
|
||||
'Only valid image files (jpg, jpeg, png or gif) '.
|
||||
'will be accepted.';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!$errors) {
|
||||
$project->save();
|
||||
$profile->setProjectPHID($project->getPHID());
|
||||
@@ -92,20 +106,32 @@ class PhabricatorProjectEditController
|
||||
$form
|
||||
->setUser($user)
|
||||
->setAction($action)
|
||||
->setEncType('multipart/form-data')
|
||||
->appendChild(
|
||||
id(new AphrontFormTextControl())
|
||||
->setLabel('Name')
|
||||
->setName('name')
|
||||
->setValue($project->getName())
|
||||
->setError($e_name))
|
||||
->appendChild(
|
||||
id(new AphrontFormSelectControl())
|
||||
->setLabel('Project Status')
|
||||
->setName('status')
|
||||
->setOptions($options)
|
||||
->setValue($project->getStatus()))
|
||||
->appendChild(
|
||||
id(new AphrontFormTextAreaControl())
|
||||
->setLabel('Blurb')
|
||||
->setName('blurb')
|
||||
->setValue($profile->getBlurb()))
|
||||
->appendChild(
|
||||
id(new AphrontFormFileControl())
|
||||
->setLabel('Change Image')
|
||||
->setName('image')
|
||||
->setCaption('Upload a 280px-wide image.'))
|
||||
->appendChild(
|
||||
id(new AphrontFormSubmitControl())
|
||||
->addCancelButton('/project/')
|
||||
->addCancelButton('/project/view/'.$project->getID().'/')
|
||||
->setValue('Save'));
|
||||
|
||||
$panel = new AphrontPanelView();
|
||||
@@ -122,5 +148,4 @@ class PhabricatorProjectEditController
|
||||
'title' => $title,
|
||||
));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -8,10 +8,14 @@
|
||||
|
||||
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/project/constants/status');
|
||||
phutil_require_module('phabricator', 'applications/project/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/project/storage/profile');
|
||||
phutil_require_module('phabricator', 'applications/project/storage/project');
|
||||
phutil_require_module('phabricator', 'view/form/base');
|
||||
phutil_require_module('phabricator', 'view/form/control/file');
|
||||
phutil_require_module('phabricator', 'view/form/control/select');
|
||||
phutil_require_module('phabricator', 'view/form/control/submit');
|
||||
phutil_require_module('phabricator', 'view/form/control/text');
|
||||
phutil_require_module('phabricator', 'view/form/control/textarea');
|
||||
@@ -21,4 +25,4 @@ phutil_require_module('phabricator', 'view/layout/panel');
|
||||
phutil_require_module('phutil', 'utils');
|
||||
|
||||
|
||||
phutil_require_source('PhabricatorProjectEditController.php');
|
||||
phutil_require_source('PhabricatorProjectProfileEditController.php');
|
||||
@@ -28,10 +28,10 @@ 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'));
|
||||
@@ -46,7 +46,6 @@ class PhabricatorProjectQuickCreateController
|
||||
|
||||
if (!$errors) {
|
||||
$project->save();
|
||||
|
||||
$profile->setProjectPHID($project->getPHID());
|
||||
$profile->save();
|
||||
|
||||
@@ -91,5 +90,4 @@ class PhabricatorProjectQuickCreateController
|
||||
|
||||
return id(new AphrontDialogResponse())->setDialog($dialog);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
phutil_require_module('phabricator', 'aphront/response/ajax');
|
||||
phutil_require_module('phabricator', 'aphront/response/dialog');
|
||||
phutil_require_module('phabricator', 'applications/project/constants/status');
|
||||
phutil_require_module('phabricator', 'applications/project/controller/base');
|
||||
phutil_require_module('phabricator', 'applications/project/storage/profile');
|
||||
phutil_require_module('phabricator', 'applications/project/storage/project');
|
||||
|
||||
Reference in New Issue
Block a user