Basic storage of relations for new issues has been done; the relations
are also properly displayed at the left side in the issue's detail view.
This commit is contained in:
parent
bcba64b2a1
commit
16dda0743c
@ -36,6 +36,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
|||||||
public $user = null;
|
public $user = null;
|
||||||
public $project = null;
|
public $project = null;
|
||||||
public $show_full = false;
|
public $show_full = false;
|
||||||
|
public $relation_types = null;
|
||||||
|
|
||||||
public function initFields($extra=array())
|
public function initFields($extra=array())
|
||||||
{
|
{
|
||||||
@ -45,9 +46,12 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
|||||||
or $this->user->hasPerm('IDF.project-member', $this->project)) {
|
or $this->user->hasPerm('IDF.project-member', $this->project)) {
|
||||||
$this->show_full = true;
|
$this->show_full = true;
|
||||||
}
|
}
|
||||||
|
$this->relation_types = $this->project->getRelationsFromConfig();
|
||||||
|
|
||||||
$contentTemplate = $this->project->getConf()->getVal(
|
$contentTemplate = $this->project->getConf()->getVal(
|
||||||
'labels_issue_template', IDF_Form_IssueTrackingConf::init_template
|
'labels_issue_template', IDF_Form_IssueTrackingConf::init_template
|
||||||
);
|
);
|
||||||
|
|
||||||
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
|
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => true,
|
array('required' => true,
|
||||||
'label' => __('Summary'),
|
'label' => __('Summary'),
|
||||||
@ -109,11 +113,10 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$relation_types = $extra['project']->getRelationsFromConfig();
|
|
||||||
$this->fields['relation_type'] = new Pluf_Form_Field_Varchar(
|
$this->fields['relation_type'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => false,
|
array('required' => false,
|
||||||
'label' => __('This issue'),
|
'label' => __('This issue'),
|
||||||
'initial' => $relation_types[0],
|
'initial' => current($this->relation_types),
|
||||||
'widget_attrs' => array('size' => 15),
|
'widget_attrs' => array('size' => 15),
|
||||||
));
|
));
|
||||||
|
|
||||||
@ -250,6 +253,49 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
|||||||
return $this->cleaned_data['status'];
|
return $this->cleaned_data['status'];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function clean_relation_type()
|
||||||
|
{
|
||||||
|
$relation_type = trim($this->cleaned_data['relation_type']);
|
||||||
|
if (empty($relation_type))
|
||||||
|
return '';
|
||||||
|
|
||||||
|
$found = false;
|
||||||
|
foreach ($this->relation_types as $type) {
|
||||||
|
if ($type == $relation_type) {
|
||||||
|
$found = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!$found) {
|
||||||
|
throw new Pluf_Form_Invalid(__('You provided an invalid relation type.'));
|
||||||
|
}
|
||||||
|
return $relation_type;
|
||||||
|
}
|
||||||
|
|
||||||
|
function clean_relation_issue()
|
||||||
|
{
|
||||||
|
$issues = trim($this->cleaned_data['relation_issue']);
|
||||||
|
if (empty($issues))
|
||||||
|
return '';
|
||||||
|
|
||||||
|
$issue_ids = preg_split('/\s*,\s*/', $issues, -1, PREG_SPLIT_NO_EMPTY);
|
||||||
|
foreach ($issue_ids as $issue_id) {
|
||||||
|
if (!ctype_digit($issue_id) || (int)$issue_id < 1) {
|
||||||
|
throw new Pluf_Form_Invalid(sprintf(
|
||||||
|
__('The value "%s" is not a valid issue id.'), $issue_id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
$issue = new IDF_Issue($issue_id);
|
||||||
|
if ($issue->id != $issue_id || $issue->project != $this->project->id) {
|
||||||
|
throw new Pluf_Form_Invalid(sprintf(
|
||||||
|
__('The issue "%s" does not exist.'), $issue_id
|
||||||
|
));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return implode(', ', $issue_ids);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clean the attachments post failure.
|
* Clean the attachments post failure.
|
||||||
*/
|
*/
|
||||||
@ -313,6 +359,27 @@ class IDF_Form_IssueCreate extends Pluf_Form
|
|||||||
foreach ($tags as $tag) {
|
foreach ($tags as $tag) {
|
||||||
$issue->setAssoc($tag);
|
$issue->setAssoc($tag);
|
||||||
}
|
}
|
||||||
|
// add relations
|
||||||
|
$verb = $this->cleaned_data['relation_type'];
|
||||||
|
$other_verb = $this->relation_types[$verb];
|
||||||
|
$related_issues = preg_split('/\s*,\s*/', $this->cleaned_data['relation_issue'], -1, PREG_SPLIT_NO_EMPTY);
|
||||||
|
foreach ($related_issues as $related_issue_id) {
|
||||||
|
$related_issue = new IDF_Issue($related_issue_id);
|
||||||
|
$rel = new IDF_IssueRelation();
|
||||||
|
$rel->issue = $issue;
|
||||||
|
$rel->verb = $verb;
|
||||||
|
$rel->other_issue = $related_issue;
|
||||||
|
$rel->submitter = $this->user;
|
||||||
|
$rel->create();
|
||||||
|
|
||||||
|
$other_rel = new IDF_IssueRelation();
|
||||||
|
$other_rel->issue = $related_issue;
|
||||||
|
$other_rel->verb = $other_verb;
|
||||||
|
$other_rel->other_issue = $issue;
|
||||||
|
$other_rel->submitter = $this->user;
|
||||||
|
$other_rel->create();
|
||||||
|
}
|
||||||
|
|
||||||
// add the first comment
|
// add the first comment
|
||||||
$comment = new IDF_IssueComment();
|
$comment = new IDF_IssueComment();
|
||||||
$comment->issue = $issue;
|
$comment->issue = $issue;
|
||||||
|
@ -72,10 +72,24 @@ Performance = Performance issue
|
|||||||
Usability = Affects program usability
|
Usability = Affects program usability
|
||||||
Maintainability = Hinders future changes';
|
Maintainability = Hinders future changes';
|
||||||
const init_one_max = 'Type, Priority, Milestone';
|
const init_one_max = 'Type, Priority, Milestone';
|
||||||
|
// ATTENTION: if you change something here, change the values below as well!
|
||||||
const init_relations = 'is related to
|
const init_relations = 'is related to
|
||||||
blocks, is blocked by
|
blocks, is blocked by
|
||||||
duplicates, is duplicated by';
|
duplicates, is duplicated by';
|
||||||
|
|
||||||
|
// These are actually all noop's, but we have no other chance to
|
||||||
|
// tell IDF's translation mechanism to mark the strings as translatable
|
||||||
|
// FIXME: IDF should get a internal translation system for strings like
|
||||||
|
// that, that can also be easily expanded by users
|
||||||
|
private function noop()
|
||||||
|
{
|
||||||
|
__('is related to');
|
||||||
|
__('blocks');
|
||||||
|
__('is blocked by');
|
||||||
|
__('duplicates');
|
||||||
|
__('is duplicated by');
|
||||||
|
}
|
||||||
|
|
||||||
public function initFields($extra=array())
|
public function initFields($extra=array())
|
||||||
{
|
{
|
||||||
$this->fields['labels_issue_template'] = new Pluf_Form_Field_Varchar(
|
$this->fields['labels_issue_template'] = new Pluf_Form_Field_Varchar(
|
||||||
|
@ -39,6 +39,7 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
|
|||||||
or $this->user->hasPerm('IDF.project-member', $this->project)) {
|
or $this->user->hasPerm('IDF.project-member', $this->project)) {
|
||||||
$this->show_full = true;
|
$this->show_full = true;
|
||||||
}
|
}
|
||||||
|
$this->relation_types = $this->project->getRelationsFromConfig();
|
||||||
if ($this->show_full) {
|
if ($this->show_full) {
|
||||||
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
|
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => true,
|
array('required' => true,
|
||||||
@ -103,11 +104,10 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
|
|||||||
),
|
),
|
||||||
));
|
));
|
||||||
|
|
||||||
$relation_types = $extra['project']->getRelationsFromConfig();
|
|
||||||
$this->fields['relation_type'] = new Pluf_Form_Field_Varchar(
|
$this->fields['relation_type'] = new Pluf_Form_Field_Varchar(
|
||||||
array('required' => false,
|
array('required' => false,
|
||||||
'label' => __('This issue'),
|
'label' => __('This issue'),
|
||||||
'initial' => $relation_types[0],
|
'initial' => current($this->relation_types),
|
||||||
'widget_attrs' => array('size' => 15),
|
'widget_attrs' => array('size' => 15),
|
||||||
));
|
));
|
||||||
|
|
||||||
|
@ -169,6 +169,24 @@ class IDF_Issue extends Pluf_Model
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function getGroupedRelatedIssues($opts = array())
|
||||||
|
{
|
||||||
|
$rels = $this->get_related_issues_list(array_merge($opts, array(
|
||||||
|
'view' => 'with_other_issue',
|
||||||
|
)));
|
||||||
|
|
||||||
|
$res = array();
|
||||||
|
foreach ($rels as $rel) {
|
||||||
|
$verb = $rel->verb;
|
||||||
|
if (!array_key_exists($verb, $res)) {
|
||||||
|
$res[$verb] = array();
|
||||||
|
}
|
||||||
|
$res[$verb][] = $rel;
|
||||||
|
}
|
||||||
|
|
||||||
|
return $res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an HTML fragment used to display this issue in the
|
* Returns an HTML fragment used to display this issue in the
|
||||||
* timeline.
|
* timeline.
|
||||||
|
@ -45,7 +45,7 @@ class IDF_IssueRelation extends Pluf_Model
|
|||||||
'model' => 'IDF_Issue',
|
'model' => 'IDF_Issue',
|
||||||
'blank' => false,
|
'blank' => false,
|
||||||
'verbose' => __('issue'),
|
'verbose' => __('issue'),
|
||||||
'relate_name' => 'issues',
|
'relate_name' => 'related_issues',
|
||||||
),
|
),
|
||||||
'verb' =>
|
'verb' =>
|
||||||
array(
|
array(
|
||||||
@ -59,7 +59,7 @@ class IDF_IssueRelation extends Pluf_Model
|
|||||||
'model' => 'IDF_Issue',
|
'model' => 'IDF_Issue',
|
||||||
'blank' => false,
|
'blank' => false,
|
||||||
'verbose' => __('other issue'),
|
'verbose' => __('other issue'),
|
||||||
'relate_name' => 'other_issues',
|
'relate_name' => 'related_other_issues',
|
||||||
),
|
),
|
||||||
'submitter' =>
|
'submitter' =>
|
||||||
array(
|
array(
|
||||||
@ -82,6 +82,13 @@ class IDF_IssueRelation extends Pluf_Model
|
|||||||
'type' => 'normal',
|
'type' => 'normal',
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
$issuetbl = $this->_con->pfx.'idf_issues';
|
||||||
|
$this->_a['views'] = array(
|
||||||
|
'with_other_issue' => array(
|
||||||
|
'join' => 'INNER JOIN '.$issuetbl.' ON other_issue='.$issuetbl.'.id',
|
||||||
|
'select' => $this->getSelect().', summary',
|
||||||
|
'props' => array('summary' => 'other_summary'),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
function preSave($create=false)
|
function preSave($create=false)
|
||||||
|
@ -234,7 +234,10 @@ class IDF_Project extends Pluf_Model
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a list of relations which are available in this project
|
* Returns a list of relations which are available in this project as
|
||||||
|
* associative array. Each key-value pair marks a set of orthogonal
|
||||||
|
* relations. To ease processing, each of these pairs is included twice
|
||||||
|
* in the array, once as key1 => key2 and once as key2 => key1.
|
||||||
*
|
*
|
||||||
* @return array List of relation names
|
* @return array List of relation names
|
||||||
*/
|
*/
|
||||||
@ -244,7 +247,11 @@ class IDF_Project extends Pluf_Model
|
|||||||
$rel = $conf->getVal('issue_relations', IDF_Form_IssueTrackingConf::init_relations);
|
$rel = $conf->getVal('issue_relations', IDF_Form_IssueTrackingConf::init_relations);
|
||||||
$relations = array();
|
$relations = array();
|
||||||
foreach (preg_split("/\015\012|\015|\012/", $rel, -1, PREG_SPLIT_NO_EMPTY) as $s) {
|
foreach (preg_split("/\015\012|\015|\012/", $rel, -1, PREG_SPLIT_NO_EMPTY) as $s) {
|
||||||
$relations = array_merge($relations, preg_split("/\s*,\s*/", $s, 2));
|
$verbs = preg_split("/\s*,\s*/", $s, 2);
|
||||||
|
if (count($verbs) == 1)
|
||||||
|
$relations += array($verbs[0] => $verbs[0]);
|
||||||
|
else
|
||||||
|
$relations += array($verbs[0] => $verbs[1], $verbs[1] => $verbs[0]);
|
||||||
}
|
}
|
||||||
return $relations;
|
return $relations;
|
||||||
}
|
}
|
||||||
|
@ -404,6 +404,8 @@ class IDF_Views_Issue
|
|||||||
$issue = Pluf_Shortcuts_GetObjectOr404('IDF_Issue', $match[2]);
|
$issue = Pluf_Shortcuts_GetObjectOr404('IDF_Issue', $match[2]);
|
||||||
$prj->inOr404($issue);
|
$prj->inOr404($issue);
|
||||||
$comments = $issue->get_comments_list(array('order' => 'id ASC'));
|
$comments = $issue->get_comments_list(array('order' => 'id ASC'));
|
||||||
|
$related_issues = $issue->getGroupedRelatedIssues(array('order' => 'creation_dtime DESC'));
|
||||||
|
|
||||||
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
|
$url = Pluf_HTTP_URL_urlForView('IDF_Views_Issue::view',
|
||||||
array($prj->shortname, $issue->id));
|
array($prj->shortname, $issue->id));
|
||||||
$title = Pluf_Template::markSafe(sprintf(__('Issue <a href="%s">%d</a>: %s'), $url, $issue->id, $issue->summary));
|
$title = Pluf_Template::markSafe(sprintf(__('Issue <a href="%s">%d</a>: %s'), $url, $issue->id, $issue->summary));
|
||||||
@ -471,7 +473,8 @@ class IDF_Views_Issue
|
|||||||
'preview' => $preview,
|
'preview' => $preview,
|
||||||
'interested' => $interested->count(),
|
'interested' => $interested->count(),
|
||||||
'previous_issue_id' => $previous_issue_id,
|
'previous_issue_id' => $previous_issue_id,
|
||||||
'next_issue_id' => $next_issue_id
|
'next_issue_id' => $next_issue_id,
|
||||||
|
'related_issues' => $related_issues,
|
||||||
),
|
),
|
||||||
$arrays),
|
$arrays),
|
||||||
$request);
|
$request);
|
||||||
|
@ -170,6 +170,19 @@
|
|||||||
<span class="label"><a href="{$url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></span><br />
|
<span class="label"><a href="{$url}" class="label"><strong>{$tag.class}:</strong>{$tag.name}</a></span><br />
|
||||||
{/foreach}
|
{/foreach}
|
||||||
</p>{/if}
|
</p>{/if}
|
||||||
|
{if count($related_issues) > 0}
|
||||||
|
{foreach $related_issues as $verb => $rel_issues}
|
||||||
|
<strong>{blocktrans}This issue {$verb}{/blocktrans}</strong><br />
|
||||||
|
{foreach $rel_issues as $rel_issue}
|
||||||
|
<span class="label">
|
||||||
|
<a href="{url 'IDF_Views_Issue::view', array($project.shortname, $rel_issue.other_issue)}"
|
||||||
|
title="{$rel_issue.other_summary}">
|
||||||
|
<strong>{$rel_issue.other_issue}</strong> - {$rel_issue.other_summary|shorten:30}
|
||||||
|
</a>
|
||||||
|
</span><br />
|
||||||
|
{/foreach}
|
||||||
|
{/foreach}
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/block}
|
{/block}
|
||||||
{block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'}
|
{block javascript}{if $form}{include 'idf/issues/js-autocomplete.html'}
|
||||||
|
Loading…
Reference in New Issue
Block a user