diff --git a/src/IDF/Form/IssueCreate.php b/src/IDF/Form/IssueCreate.php
index c6f3aef..859adb1 100644
--- a/src/IDF/Form/IssueCreate.php
+++ b/src/IDF/Form/IssueCreate.php
@@ -36,6 +36,7 @@ class IDF_Form_IssueCreate extends Pluf_Form
public $user = null;
public $project = null;
public $show_full = false;
+ public $relation_types = null;
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)) {
$this->show_full = true;
}
+ $this->relation_types = $this->project->getRelationsFromConfig();
+
$contentTemplate = $this->project->getConf()->getVal(
'labels_issue_template', IDF_Form_IssueTrackingConf::init_template
);
+
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
array('required' => true,
'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(
array('required' => false,
'label' => __('This issue'),
- 'initial' => $relation_types[0],
+ 'initial' => current($this->relation_types),
'widget_attrs' => array('size' => 15),
));
@@ -250,6 +253,49 @@ class IDF_Form_IssueCreate extends Pluf_Form
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.
*/
@@ -313,6 +359,27 @@ class IDF_Form_IssueCreate extends Pluf_Form
foreach ($tags as $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
$comment = new IDF_IssueComment();
$comment->issue = $issue;
diff --git a/src/IDF/Form/IssueTrackingConf.php b/src/IDF/Form/IssueTrackingConf.php
index c62a10d..738c3e4 100644
--- a/src/IDF/Form/IssueTrackingConf.php
+++ b/src/IDF/Form/IssueTrackingConf.php
@@ -72,10 +72,24 @@ Performance = Performance issue
Usability = Affects program usability
Maintainability = Hinders future changes';
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
blocks, is blocked 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())
{
$this->fields['labels_issue_template'] = new Pluf_Form_Field_Varchar(
diff --git a/src/IDF/Form/IssueUpdate.php b/src/IDF/Form/IssueUpdate.php
index 9835bc3..4571ab1 100644
--- a/src/IDF/Form/IssueUpdate.php
+++ b/src/IDF/Form/IssueUpdate.php
@@ -39,6 +39,7 @@ class IDF_Form_IssueUpdate extends IDF_Form_IssueCreate
or $this->user->hasPerm('IDF.project-member', $this->project)) {
$this->show_full = true;
}
+ $this->relation_types = $this->project->getRelationsFromConfig();
if ($this->show_full) {
$this->fields['summary'] = new Pluf_Form_Field_Varchar(
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(
array('required' => false,
'label' => __('This issue'),
- 'initial' => $relation_types[0],
+ 'initial' => current($this->relation_types),
'widget_attrs' => array('size' => 15),
));
diff --git a/src/IDF/Issue.php b/src/IDF/Issue.php
index b173209..6346621 100644
--- a/src/IDF/Issue.php
+++ b/src/IDF/Issue.php
@@ -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
* timeline.
diff --git a/src/IDF/IssueRelation.php b/src/IDF/IssueRelation.php
index f918631..b0048dd 100644
--- a/src/IDF/IssueRelation.php
+++ b/src/IDF/IssueRelation.php
@@ -45,7 +45,7 @@ class IDF_IssueRelation extends Pluf_Model
'model' => 'IDF_Issue',
'blank' => false,
'verbose' => __('issue'),
- 'relate_name' => 'issues',
+ 'relate_name' => 'related_issues',
),
'verb' =>
array(
@@ -59,7 +59,7 @@ class IDF_IssueRelation extends Pluf_Model
'model' => 'IDF_Issue',
'blank' => false,
'verbose' => __('other issue'),
- 'relate_name' => 'other_issues',
+ 'relate_name' => 'related_other_issues',
),
'submitter' =>
array(
@@ -82,6 +82,13 @@ class IDF_IssueRelation extends Pluf_Model
'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)
diff --git a/src/IDF/Project.php b/src/IDF/Project.php
index 211a9ed..2f09219 100644
--- a/src/IDF/Project.php
+++ b/src/IDF/Project.php
@@ -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
*/
@@ -244,7 +247,11 @@ class IDF_Project extends Pluf_Model
$rel = $conf->getVal('issue_relations', IDF_Form_IssueTrackingConf::init_relations);
$relations = array();
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;
}
diff --git a/src/IDF/Views/Issue.php b/src/IDF/Views/Issue.php
index 21800ff..90c8de8 100644
--- a/src/IDF/Views/Issue.php
+++ b/src/IDF/Views/Issue.php
@@ -404,6 +404,8 @@ class IDF_Views_Issue
$issue = Pluf_Shortcuts_GetObjectOr404('IDF_Issue', $match[2]);
$prj->inOr404($issue);
$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',
array($prj->shortname, $issue->id));
$title = Pluf_Template::markSafe(sprintf(__('Issue %d: %s'), $url, $issue->id, $issue->summary));
@@ -471,7 +473,8 @@ class IDF_Views_Issue
'preview' => $preview,
'interested' => $interested->count(),
'previous_issue_id' => $previous_issue_id,
- 'next_issue_id' => $next_issue_id
+ 'next_issue_id' => $next_issue_id,
+ 'related_issues' => $related_issues,
),
$arrays),
$request);
diff --git a/src/IDF/templates/idf/issues/view.html b/src/IDF/templates/idf/issues/view.html
index e27be87..069cd0b 100644
--- a/src/IDF/templates/idf/issues/view.html
+++ b/src/IDF/templates/idf/issues/view.html
@@ -170,6 +170,19 @@
{$tag.class}:{$tag.name}
{/foreach}