Page MenuHomestyx hydra

No OneTemporary

diff --git a/resources/sql/patches/064.subporjects.sql b/resources/sql/patches/064.subporjects.sql
new file mode 100644
index 0000000000..379e1bdc77
--- /dev/null
+++ b/resources/sql/patches/064.subporjects.sql
@@ -0,0 +1,11 @@
+ALTER TABLE phabricator_project.project
+ ADD subprojectPHIDs longblob NOT NULL;
+UPDATE phabricator_project.project
+ SET subprojectPHIDs = '[]';
+
+CREATE TABLE phabricator_project.project_subproject (
+ projectPHID varchar(64) BINARY NOT NULL,
+ subprojectPHID varchar(64) BINARY NOT NULL,
+ PRIMARY KEY (subprojectPHID, projectPHID),
+ UNIQUE KEY (projectPHID, subprojectPHID)
+);
\ No newline at end of file
diff --git a/src/applications/project/controller/list/PhabricatorProjectListController.php b/src/applications/project/controller/list/PhabricatorProjectListController.php
index 05faacf307..25eaf353c7 100644
--- a/src/applications/project/controller/list/PhabricatorProjectListController.php
+++ b/src/applications/project/controller/list/PhabricatorProjectListController.php
@@ -1,136 +1,134 @@
<?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.
*/
class PhabricatorProjectListController
extends PhabricatorProjectController {
public function processRequest() {
$projects = id(new PhabricatorProject())->loadAllWhere(
'1 = 1 ORDER BY id DESC limit 100');
$project_phids = mpull($projects, 'getPHID');
$profiles = array();
if ($projects) {
$profiles = id(new PhabricatorProjectProfile())->loadAllWhere(
'projectPHID in (%Ls)',
$project_phids);
$profiles = mpull($profiles, null, 'getProjectPHID');
}
$affil_groups = array();
if ($projects) {
$affil_groups = PhabricatorProjectAffiliation::loadAllForProjectPHIDs(
$project_phids);
}
$author_phids = mpull($projects, 'getAuthorPHID');
$handles = id(new PhabricatorObjectHandleData($author_phids))
->loadHandles();
- $project_phids = mpull($projects, 'getPHID');
-
$query = id(new ManiphestTaskQuery())
->withProjects($project_phids)
->withAnyProject(true)
->withStatus(ManiphestTaskQuery::STATUS_OPEN)
->setLimit(PHP_INT_MAX);
$tasks = $query->execute();
$groups = array();
foreach ($tasks as $task) {
foreach ($task->getProjectPHIDs() as $phid) {
$groups[$phid][] = $task;
}
}
$rows = array();
foreach ($projects as $project) {
$phid = $project->getPHID();
$profile = $profiles[$phid];
$affiliations = $affil_groups[$phid];
$group = idx($groups, $phid, array());
$task_count = count($group);
$population = count($affiliations);
$status = PhabricatorProjectStatus::getNameForStatus(
$project->getStatus());
$blurb = $profile->getBlurb();
$blurb = phutil_utf8_shorten($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_render_tag(
'a',
array(
'href' => '/maniphest/view/all/?projects='.$phid,
),
phutil_escape_html($task_count)),
phutil_render_tag(
'a',
array(
'class' => 'small grey button',
'href' => '/project/view/'.$project->getID().'/',
),
'View Project Profile'),
);
}
$table = new AphrontTableView($rows);
$table->setHeaders(
array(
'Project',
'Description',
'Mastermind',
'Population',
'Status',
'Open Tasks',
'',
));
$table->setColumnClasses(
array(
'pri',
'wide',
'',
'right',
'',
'right',
'action',
));
$panel = new AphrontPanelView();
$panel->appendChild($table);
$panel->setHeader('Project');
$panel->setCreateButton('Create New Project', '/project/create/');
return $this->buildStandardPageResponse(
$panel,
array(
'title' => 'Projects',
));
}
}
diff --git a/src/applications/project/controller/profile/PhabricatorProjectProfileController.php b/src/applications/project/controller/profile/PhabricatorProjectProfileController.php
index 01d336326c..ed5ac31534 100644
--- a/src/applications/project/controller/profile/PhabricatorProjectProfileController.php
+++ b/src/applications/project/controller/profile/PhabricatorProjectProfileController.php
@@ -1,237 +1,297 @@
<?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.
*/
class PhabricatorProjectProfileController
extends PhabricatorProjectController {
private $id;
private $page;
public function willProcessRequest(array $data) {
$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 = $project->loadProfile();
if (!$profile) {
$profile = new PhabricatorProjectProfile();
}
$src_phid = $profile->getProfileImagePHID();
if (!$src_phid) {
$src_phid = $user->getProfileImagePHID();
}
$picture = PhabricatorFileURI::getViewURIForPHID($src_phid);
$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',
+ 'edit' => 'Edit Project',
'affiliation' => 'Edit Affiliation',
);
if (empty($pages[$this->page])) {
- $this->page = 'action'; // key($pages);
+ $this->page = 'action';
}
switch ($this->page) {
default:
$content = $this->renderBasicInformation($project, $profile);
break;
}
$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)));
}
}
$profile->appendChild($content);
return $this->buildStandardPageResponse(
$profile,
array(
'title' => $project->getName(),
));
}
+ //----------------------------------------------------------------------------
+ // Helper functions
+
private function renderBasicInformation($project, $profile) {
$blurb = nonempty(
$profile->getBlurb(),
'//Nothing is known about this elusive project.//');
$engine = PhabricatorMarkupEngine::newProfileMarkupEngine();
$blurb = $engine->markupText($blurb);
$affiliations = $project->loadAffiliations();
$phids = array_merge(
array($project->getAuthorPHID()),
- mpull($affiliations, 'getUserPHID'));
+ $project->getSubprojectPHIDs(),
+ mpull($affiliations, 'getUserPHID')
+ );
+ $phids = array_unique($phids);
$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.' &mdash; '.$role.$status.'</li>';
}
+
if ($affiliated) {
$affiliated = '<ul>'.implode("\n", $affiliated).'</ul>';
} else {
$affiliated = '<p><em>No one is affiliated with this project.</em></p>';
}
+ if ($project->getSubprojectPHIDs()) {
+ $table = $this->renderSubprojectTable(
+ $handles,
+ $project->getSubprojectPHIDs());
+ $subproject_list = $table->render();
+ } else {
+ $subproject_list =
+ '<p><em>There are no projects attached for such specie.</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">'.
+ '<div class="phabricator-profile-info-group">'.
+ '<h1 class="phabricator-profile-info-header">Resources</h1>'.
+ '<div class="phabricator-profile-info-pane">'.
$affiliated.
- '</div>
- </div>';
+ '</div>'.
+ '</div>';
+
+ $content .= '<div class="phabricator-profile-info-group">'.
+ '<h1 class="phabricator-profile-info-header">Subprojects</h1>'.
+ '<div class="phabricator-profile-info-pane">'.
+ $subproject_list.
+ '</div>'.
+ '</div>';
$query = id(new ManiphestTaskQuery())
->withProjects(array($project->getPHID()))
->withStatus(ManiphestTaskQuery::STATUS_OPEN)
->setOrderBy(ManiphestTaskQuery::ORDER_PRIORITY)
->setLimit(10)
->setCalculateRows(true);
$tasks = $query->execute();
$count = $query->getRowCount();
$phids = mpull($tasks, 'getOwnerPHID');
$phids = array_filter($phids);
$handles = id(new PhabricatorObjectHandleData($phids))
->loadHandles();
$task_views = array();
foreach ($tasks as $task) {
$view = id(new ManiphestTaskSummaryView())
->setTask($task)
->setHandles($handles)
->setUser($this->getRequest()->getUser());
$task_views[] = $view->render();
}
if (empty($tasks)) {
$task_views = '<em>No open tasks.</em>';
} else {
$task_views = implode('', $task_views);
}
$open = number_format($count);
$more_link = phutil_render_tag(
'a',
array(
'href' => '/maniphest/view/all/?projects='.$project->getPHID(),
),
"View All Open Tasks \xC2\xBB");
$content .=
'<div class="phabricator-profile-info-group">
<h1 class="phabricator-profile-info-header">'.
"Open Tasks ({$open})".
'</h1>'.
'<div class="phabricator-profile-info-pane">'.
$task_views.
'<div class="phabricator-profile-info-pane-more-link">'.
$more_link.
'</div>'.
'</div>
</div>';
return $content;
}
+
+ private function renderSubprojectTable(
+ PhabricatorObjectHandleData $handles,
+ $subprojects_phids) {
+
+ $rows = array();
+ foreach ($subprojects_phids as $subproject_phid) {
+ $phid = $handles[$subproject_phid]->getPHID();
+
+ $rows[] = array(
+ phutil_escape_html($handles[$phid]->getFullName()),
+ phutil_render_tag(
+ 'a',
+ array(
+ 'class' => 'small grey button',
+ 'href' => $handles[$phid]->getURI(),
+ ),
+ 'View Project Profile'),
+ );
+ }
+
+ $table = new AphrontTableView($rows);
+ $table->setHeaders(
+ array(
+ 'Name',
+ '',
+ ));
+ $table->setColumnClasses(
+ array(
+ 'pri',
+ 'action right',
+ ));
+
+ return $table;
+ }
}
diff --git a/src/applications/project/controller/profile/__init__.php b/src/applications/project/controller/profile/__init__.php
index 0a31f17672..b9241382bc 100644
--- a/src/applications/project/controller/profile/__init__.php
+++ b/src/applications/project/controller/profile/__init__.php
@@ -1,26 +1,27 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'aphront/response/404');
phutil_require_module('phabricator', 'applications/files/uri');
phutil_require_module('phabricator', 'applications/maniphest/query');
phutil_require_module('phabricator', 'applications/maniphest/view/tasksummary');
phutil_require_module('phabricator', 'applications/markup/engine');
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/profile');
phutil_require_module('phabricator', 'applications/project/storage/project');
+phutil_require_module('phabricator', 'view/control/table');
phutil_require_module('phabricator', 'view/layout/profile');
phutil_require_module('phabricator', 'view/utils');
phutil_require_module('phutil', 'markup');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorProjectProfileController.php');
diff --git a/src/applications/project/controller/profileedit/PhabricatorProjectProfileEditController.php b/src/applications/project/controller/profileedit/PhabricatorProjectProfileEditController.php
index 55b0c1c218..ae9710eca2 100644
--- a/src/applications/project/controller/profileedit/PhabricatorProjectProfileEditController.php
+++ b/src/applications/project/controller/profileedit/PhabricatorProjectProfileEditController.php
@@ -1,147 +1,163 @@
<?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.
*/
class PhabricatorProjectProfileEditController
extends PhabricatorProjectController {
public function willProcessRequest(array $data) {
$this->id = $data['id'];
}
public function processRequest() {
$request = $this->getRequest();
$user = $request->getUser();
$project = id(new PhabricatorProject())->load($this->id);
if (!$project) {
return new Aphront404Response();
}
$profile = $project->loadProfile();
if (empty($profile)) {
$profile = new PhabricatorProjectProfile();
}
+ if ($project->getSubprojectPHIDs()) {
+ $phids = $project->getSubprojectPHIDs();
+ $handles = id(new PhabricatorObjectHandleData($phids))
+ ->loadHandles();
+ $subprojects = mpull($handles, 'getFullName', 'getPHID');
+ } else {
+ $subprojects = array();
+ }
+
$options = PhabricatorProjectStatus::getStatusMap();
$e_name = true;
$errors = array();
if ($request->isFormPost()) {
$project->setName($request->getStr('name'));
$project->setStatus($request->getStr('status'));
+ $project->setSubprojectPHIDs($request->getArr('set_subprojects'));
$profile->setBlurb($request->getStr('blurb'));
if (!strlen($project->getName())) {
$e_name = 'Required';
$errors[] = 'Project name is required.';
} else {
$e_name = null;
}
if (!empty($_FILES['image'])) {
$err = idx($_FILES['image'], 'error');
if ($err != UPLOAD_ERR_NO_FILE) {
$file = PhabricatorFile::newFromPHPUpload(
$_FILES['image'],
array(
'authorPHID' => $user->getPHID(),
));
$okay = $file->isTransformableImage();
if ($okay) {
$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) '.
'will be accepted.';
}
}
}
if (!$errors) {
$project->save();
$profile->setProjectPHID($project->getPHID());
$profile->save();
return id(new AphrontRedirectResponse())
->setURI('/project/view/'.$project->getID().'/');
}
}
$error_view = null;
if ($errors) {
$error_view = new AphrontErrorView();
$error_view->setTitle('Form Errors');
$error_view->setErrors($errors);
}
$header_name = 'Edit Project';
$title = 'Edit Project';
$action = '/project/edit/'.$project->getID().'/';
$form = new AphrontFormView();
$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 AphrontFormTokenizerControl())
+ ->setDatasource('/typeahead/common/projects/')
+ ->setLabel('Subprojects')
+ ->setName('set_subprojects')
+ ->setValue($subprojects))
->appendChild(
id(new AphrontFormFileControl())
->setLabel('Change Image')
->setName('image'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton('/project/view/'.$project->getID().'/')
->setValue('Save'));
$panel = new AphrontPanelView();
$panel->setHeader($header_name);
- $panel->setWidth(AphrontPanelView::WIDTH_FORM);
+ $panel->setWidth(AphrontPanelView::WIDTH_WIDE);
$panel->appendChild($form);
return $this->buildStandardPageResponse(
array(
$error_view,
$panel,
),
array(
'title' => $title,
));
}
}
diff --git a/src/applications/project/controller/profileedit/__init__.php b/src/applications/project/controller/profileedit/__init__.php
index a03f8883d2..46a80365db 100644
--- a/src/applications/project/controller/profileedit/__init__.php
+++ b/src/applications/project/controller/profileedit/__init__.php
@@ -1,29 +1,31 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
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/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/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');
+phutil_require_module('phabricator', 'view/form/control/tokenizer');
phutil_require_module('phabricator', 'view/form/error');
phutil_require_module('phabricator', 'view/layout/panel');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorProjectProfileEditController.php');
diff --git a/src/applications/project/storage/project/PhabricatorProject.php b/src/applications/project/storage/project/PhabricatorProject.php
index d9da01e716..4558eb5723 100644
--- a/src/applications/project/storage/project/PhabricatorProject.php
+++ b/src/applications/project/storage/project/PhabricatorProject.php
@@ -1,49 +1,75 @@
<?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.
*/
class PhabricatorProject extends PhabricatorProjectDAO {
protected $name;
protected $phid;
protected $status = PhabricatorProjectStatus::UNKNOWN;
protected $authorPHID;
+ protected $subprojectPHIDs = array();
+
+ private $subprojectsNeedUpdate;
public function getConfiguration() {
return array(
self::CONFIG_AUX_PHID => true,
+ self::CONFIG_SERIALIZATION => array(
+ 'subprojectPHIDs' => self::SERIALIZATION_JSON,
+ ),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorPHIDConstants::PHID_TYPE_PROJ);
}
+ public function setSubprojectPHIDs(array $phids) {
+ $this->subprojectPHIDs = $phids;
+ $this->subprojectsNeedUpdate = true;
+ return $this;
+ }
+
public function loadProfile() {
$profile = id(new PhabricatorProjectProfile())->loadOneWhere(
'projectPHID = %s',
$this->getPHID());
return $profile;
}
public function loadAffiliations() {
$affils = PhabricatorProjectAffiliation::loadAllForProjectPHIDs(
array($this->getPHID()));
return $affils[$this->getPHID()];
}
+
+ public function save() {
+ $result = parent::save();
+
+ if ($this->subprojectsNeedUpdate) {
+ // If we've changed the project PHIDs for this task, update the link
+ // table.
+ PhabricatorProjectSubproject::updateProjectSubproject($this);
+ $this->subprojectsNeedUpdate = false;
+ }
+
+ return $result;
+ }
+
}
diff --git a/src/applications/project/storage/project/__init__.php b/src/applications/project/storage/project/__init__.php
index bc7599691d..12477e8dc7 100644
--- a/src/applications/project/storage/project/__init__.php
+++ b/src/applications/project/storage/project/__init__.php
@@ -1,19 +1,20 @@
<?php
/**
* This file is automatically generated. Lint this module to rebuild it.
* @generated
*/
phutil_require_module('phabricator', 'applications/phid/constants');
phutil_require_module('phabricator', 'applications/phid/storage/phid');
phutil_require_module('phabricator', 'applications/project/constants/status');
phutil_require_module('phabricator', 'applications/project/storage/affiliation');
phutil_require_module('phabricator', 'applications/project/storage/base');
phutil_require_module('phabricator', 'applications/project/storage/profile');
+phutil_require_module('phabricator', 'applications/project/storage/subproject');
phutil_require_module('phutil', 'utils');
phutil_require_source('PhabricatorProject.php');
diff --git a/src/applications/project/storage/subproject/PhabricatorProjectSubproject.php b/src/applications/project/storage/subproject/PhabricatorProjectSubproject.php
new file mode 100644
index 0000000000..43832e7275
--- /dev/null
+++ b/src/applications/project/storage/subproject/PhabricatorProjectSubproject.php
@@ -0,0 +1,67 @@
+<?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.
+ */
+
+/**
+ * This is a DAO for the Task -> Project table, which denormalizes the
+ * relationship between tasks and projects into a link table so it can be
+ * efficiently queried. This table is not authoritative; the projectPHIDs field
+ * of ManiphestTask is. The rows in this table are regenerated when transactions
+ * are applied to tasks which affected their associated projects.
+ *
+ * @group maniphest
+ */
+final class PhabricatorProjectSubproject extends PhabricatorProjectDAO {
+
+ protected $projectPHID;
+ protected $subprojectPHID;
+
+ public function getConfiguration() {
+ return array(
+ self::CONFIG_IDS => self::IDS_MANUAL,
+ self::CONFIG_TIMESTAMPS => false,
+ );
+ }
+
+ public static function updateProjectSubproject(PhabricatorProject $project) {
+ $dao = new PhabricatorProjectSubproject();
+ $conn = $dao->establishConnection('w');
+
+ $sql = array();
+ foreach ($project->getSubprojectPHIDs() as $subproject_phid) {
+ $sql[] = qsprintf(
+ $conn,
+ '(%s, %s)',
+ $project->getPHID(),
+ $subproject_phid);
+ }
+
+ queryfx(
+ $conn,
+ 'DELETE FROM %T WHERE projectPHID = %s',
+ $dao->getTableName(),
+ $project->getPHID());
+ if ($sql) {
+ queryfx(
+ $conn,
+ 'INSERT INTO %T (projectPHID, subprojectPHID) VALUES %Q',
+ $dao->getTableName(),
+ implode(', ', $sql));
+ }
+ }
+
+}
diff --git a/src/applications/project/storage/subproject/__init__.php b/src/applications/project/storage/subproject/__init__.php
new file mode 100644
index 0000000000..cffb2495f9
--- /dev/null
+++ b/src/applications/project/storage/subproject/__init__.php
@@ -0,0 +1,14 @@
+<?php
+/**
+ * This file is automatically generated. Lint this module to rebuild it.
+ * @generated
+ */
+
+
+
+phutil_require_module('phabricator', 'applications/project/storage/base');
+phutil_require_module('phabricator', 'storage/qsprintf');
+phutil_require_module('phabricator', 'storage/queryfx');
+
+
+phutil_require_source('PhabricatorProjectSubproject.php');
\ No newline at end of file

File Metadata

Mime Type
text/x-diff
Expires
Fri, Oct 31, 3:54 AM (7 h, 35 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
311977
Default Alt Text
(29 KB)

Event Timeline