[Fusionforge-commits] FusionForge branch master updated. 6.0.4-1083-g8a1b4ab

Franck Villaume nerville at libremir.placard.fr.eu.org
Sat Oct 22 14:36:39 CEST 2016


This is an automated email from the git hooks/post-receive script. It was
generated because a ref change was pushed to the repository containing
the project "FusionForge".

The branch, master has been updated
       via  8a1b4ab973b83396a28b2283f5f48cad8d25e185 (commit)
       via  d01917cea8b9d1b62e5af10b5beec14a9cd6d2ea (commit)
      from  61594276c4675d7f350d525e69e6ce0e18c9bae2 (commit)

Those revisions listed above that are new to this repository have
not appeared on any other notification email; so we list those
revisions in full, below.

- Log -----------------------------------------------------------------
https://scm.fusionforge.org/anonscm/gitweb/?p=fusionforge/fusionforge.git;a=commitdiff;h=8a1b4ab973b83396a28b2283f5f48cad8d25e185

commit 8a1b4ab973b83396a28b2283f5f48cad8d25e185
Author: Franck Villaume <franck.villaume at trivialdev.com>
Date:   Sat Oct 22 14:35:27 2016 +0200

    first implementation of Object Association: to support Artifact - Document association

diff --git a/src/CHANGES b/src/CHANGES
index 5abe328..5793049 100644
--- a/src/CHANGES
+++ b/src/CHANGES
@@ -10,6 +10,7 @@ FusionForge 6.X:
 * Docman: support private directory. (TrivialDev)
 * Docman: support document versioning. (TrivialDev)
 * Docman: support cross ref. forum, documents, task or artifact. (TrivialDev)
+* Core System: support object association n-n, bidirectional (Artifact, Document) (TrivialDev)
 * FRS: link package release to tracker roadmap. (TrivialDev)
 * Layout: new dynamic quickNav menu: based on user activity to select 5 more visited projects (TrivialDev)
 * Plugin AuthBuiltin: add captcha after 3 attempts with the same login [#795] (TrivialDev)
diff --git a/src/common/docman/Document.class.php b/src/common/docman/Document.class.php
index 974d69a..d6c26fd 100644
--- a/src/common/docman/Document.class.php
+++ b/src/common/docman/Document.class.php
@@ -26,7 +26,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-require_once $gfcommon.'include/FFError.class.php';
+require_once $gfcommon.'include/FFObject.class.php';
 require_once $gfcommon.'docman/Parsedata.class.php';
 require_once $gfcommon.'docman/DocumentManager.class.php';
 require_once $gfcommon.'docman/DocumentGroup.class.php';
@@ -48,25 +48,29 @@ $DOCUMENT_OBJ = array();
  * @param	int|bool	$res		The result set handle ("SELECT * FROM docdata_vw WHERE docid=$1")
  * @return	Document	a document object or false on failure
  */
-function &document_get_object($doc_id, $group_id, $res = false) {
+function &document_get_object($doc_id, $group_id = false, $res = false) {
 	global $DOCUMENT_OBJ;
 	if (!isset($DOCUMENT_OBJ["_".$doc_id."_"])) {
 		if ($res) {
 			//the db result handle was passed in
-		} else {
+		} elseif ($group_id) {
 			$res = db_query_params('SELECT * FROM docdata_vw WHERE docid = $1 and group_id = $2',
 						array($doc_id, $group_id));
+		} else {
+			$res = db_query_params('SELECT * FROM docdata_vw WHERE docid = $1',
+						array($doc_id));
 		}
 		if (!$res || db_numrows($res) < 1) {
 			$DOCUMENT_OBJ["_".$doc_id."_"] = false;
 		} else {
-			$DOCUMENT_OBJ["_".$doc_id."_"] = new Document(group_get_object($group_id), $doc_id, db_fetch_array($res));
+			$arr = db_fetch_array($res);
+			$DOCUMENT_OBJ["_".$doc_id."_"] = new Document(group_get_object($arr['group_id']), $doc_id, $arr);
 		}
 	}
 	return $DOCUMENT_OBJ["_".$doc_id."_"];
 }
 
-class Document extends FFError {
+class Document extends FFObject {
 
 	/**
 	 * Associative array of data from db.
@@ -91,7 +95,7 @@ class Document extends FFError {
 	 * @internal	param	\The $array associative array of data.
 	 */
 	function __construct(&$Group, $docid = false, $arr = false) {
-		parent::__construct();
+		parent::__construct($docid, get_class());
 		if (!$Group || !is_object($Group)) {
 			$this->setError(_('Invalid Project'));
 			return;
@@ -393,6 +397,14 @@ class Document extends FFError {
 		return $this->data_array['stateid'];
 	}
 
+	function getView() {
+		$view = 'listfile';
+		if ($this->getStateID() == 2) {
+			$view = 'listtrashfile';
+		}
+		return $view;
+	}
+
 	/**
 	 * getStateName - the statename of this document.
 	 *
@@ -1084,6 +1096,12 @@ class Document extends FFError {
 			db_rollback();
 			return false;
 		}
+
+		if (!$this->removeAllAssociations()) {
+			// error message already set by FFObject class.
+			db_rollback();
+			return false;
+		}
 		db_commit();
 
 		foreach ($serialids as $serialid) {
@@ -1201,6 +1219,10 @@ class Document extends FFError {
 		$this->sendNotice(false);
 		return true;
 	}
+
+	function getPermalink() {
+		return '/docman/d_follow.php/'.$this->getID();
+	}
 }
 
 // Local Variables:
diff --git a/src/common/include/FFObject.class.php b/src/common/include/FFObject.class.php
new file mode 100644
index 0000000..8bfd26b
--- /dev/null
+++ b/src/common/include/FFObject.class.php
@@ -0,0 +1,345 @@
+<?php
+/**
+ * FusionForge base Object class
+ *
+ * Copyright 2016, Franck Villaume - TrivialDev
+ *
+ * This file is part of FusionForge. FusionForge is free software;
+ * you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the Licence, or (at your option)
+ * any later version.
+ *
+ * FusionForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FusionForge; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+/**
+ * Any FusionForge object can be associate to another object.
+ * It helps to follow tracks of relation between elements.
+ * e.g: a document can be associate to a tracker.
+ *      a artifact can be associate to a release.
+ *
+ * This class brings generic functions to add/remove/get associations of any object.
+ */
+
+require_once $gfcommon.'include/FFError.class.php';
+
+class FFObject extends FFError {
+
+	/**
+	 * store id & objectType of association.
+	 * array[objectType] = array(id, id, ...)
+	 *
+	 * @var	array	$associatedToArray
+	 */
+	var $associatedToArray = array();
+
+	/**
+	 *
+	 * @var	array	$associatedFromArray
+	 */
+	var $associatedFromArray = array();
+
+	function __construct($id = false, $objectType = false) {
+		parent::__construct();
+		if (forge_get_config('use_object_associations') && $id && $objectType) {
+			$res = db_query_params('SELECT to_object_type, to_ref_id, to_id FROM fusionforge_object_assiociation WHERE from_id = $1 AND from_object_type = $2',
+						array($id, $objectType));
+			if ($res && db_numrows($res)) {
+				while ($arr = db_fetch_array($res)) {
+					$this->associatedToArray[$arr[0]][$arr[1]][] = $arr[2];
+				}
+			}
+			$res = db_query_params('SELECT from_object_type, from_ref_id, from_id FROM fusionforge_object_assiociation WHERE to_id = $1 AND to_object_type = $2',
+						array($id, $objectType));
+			if ($res && db_numrows($res)) {
+				while ($arr = db_fetch_array($res)) {
+					$this->associatedFromArray[$arr[0]][$arr[1]][] = $arr[2];
+				}
+			}
+		}
+		return;
+	}
+
+	function getAssociatedTo() {
+		return $this->associatedToArray;
+	}
+
+	function getAssociatedFrom() {
+		return $this->associatedFromArray;
+	}
+
+	function getRefID($object) {
+		switch (get_class($object)) {
+			case 'Document':
+				return $object->Group->getID();
+				break;
+			case 'Artifact':
+			case 'ArtifactHtml':
+				return $object->ArtifactType->getID();
+				break;
+		}
+	}
+
+	function getGroupID($object) {
+		switch (get_class($object)) {
+			case 'Document':
+				return $object->Group->getID();
+				break;
+			case 'Artifact':
+			case 'ArtifactHtml':
+				return $object->ArtifactType->Group->getID();
+				break;
+		}
+	}
+
+	function getRealClass($object) {
+		switch (get_class($object)) {
+			case 'Document':
+				return 'Document';
+				break;
+			case 'Artifact':
+			case 'ArtifactHtml':
+				return 'Artifact';
+				break;
+		}
+	}
+
+	function getRefObject($objectId, $objectRefId, $objectType) {
+		switch ($objectType) {
+			case 'Document':
+				return document_get_object($objectId, $objectRefId);
+				break;
+			case 'Artifact':
+				return artifact_get_object($objectId);
+				break;
+		}
+	}
+
+	function checkPermWrapper($objectType, $objectRefId) {
+		switch($objectType) {
+			case 'Document':
+				return forge_check_perm('docman', $objectRefId, 'read');
+				break;
+			case 'Artifact':
+			case 'ArtifactHtml':
+				return forge_check_perm('tracker', $objectRefId, 'read');
+				break;
+		}
+	}
+
+	function addAssociations($objectrefs = '') {
+		$objectRefArr = explode(',', $objectrefs);
+		$status = false;
+		if (count($objectRefArr) > 0) {
+			$statusArr = array();
+			foreach ($objectRefArr as $objectRef) {
+				if (preg_match('/^[Dd][0-9]+/', $objectRef)) {
+					//Document Ref.
+					$documentId = substr($objectRef, 1);
+					$documentObject = document_get_object($documentId, $this->getGroupID($this));
+					if (is_object($documentObject)) {
+						$statusArr[] = $this->addAssociationTo($documentObject);
+					} else {
+						$this->setError(_('Unable to retrieve object ref')._(': ').$objectRef);
+						$statusArr[] = false;
+					}
+				} elseif (preg_match('/^#[0-9]+/', $objectRef)) {
+					//Artifact Ref.
+					$artifactId = substr($objectRef, 1);
+					$artifactObject = artifact_get_object($artifactId);
+					if (is_object($artifactObject)) {
+						$statusArr[] = $this->addAssociationTo($artifactObject);
+					} else {
+						$this->setError(_('Unable to retrieve object ref')._(': ').$objectRef);
+						$statusArr[] = false;
+					}
+				} else {
+					$this->setError(_('No associate ref object found')._(': ').$objectRef);
+					$statusArr[] = false;
+				}
+			}
+		}
+		if (!in_array(false, $statusArr)) {
+			$status = true;
+		}
+		return $status;
+	}
+
+	function addAssociationTo($object) {
+		if (is_object($object)) {
+			$objectType = get_class($object);
+			$objectRefId = $this->getRefID($object);
+			if (!isset($this->associateArray[$objectType][$objectRefId][$object->getID()])) {
+				$res = db_query_params('INSERT INTO fusionforge_object_assiociation (from_id, from_object_type, from_ref_id, to_object_type, to_id, to_ref_id)
+								VALUES ($1, $2, $3, $4, $5, $6)',
+							array($this->getID(), $this->getRealClass($this), $this->getRefID($this), $objectType, $object->getID(), $objectRefId));
+				if ($res) {
+					$this->associateArray[$objectType][$objectRefId][] = $object->getID();
+					return true;
+				} else {
+					$this->setError(_('Cannot insert association')._(': ').db_error());
+				}
+			} else {
+				$this->setError(_('Association already existing'));
+			}
+		} else {
+			$this->setError(_('Cannot set association to a non-object'));
+		}
+		return false;
+	}
+
+	function removeAssociationTo($object) {
+		if (is_object($object)) {
+			$objectType = get_class($object);
+			$objectRefId = $this->getRefID($object);
+			if (isset($this->associateArray[$objectType][$objectRefId][$object->getID()])) {
+				$res = db_query_params('DELETE FROM fusionforge_object_assiociation WHERE from_id = $1 AND from_object_type = $2
+								AND to_object_type = $3 AND to_id = $4',
+							array($this->getID(), $this->getRealClass($this), $objectType, $object->getID()));
+				if ($res) {
+					unset($this->associateArray[$objectType][$objectRefId][$object->getID()]);
+					return true;
+				} else {
+					$this->setError(_('Cannot delete association')._(': ').db_error());
+				}
+			} else {
+				$this->setError(_('Association does not existing'));
+			}
+		} else {
+			$this->setError(_('Cannot remove association to a non-object'));
+		}
+		return false;
+	}
+
+	function removeAssociationFrom($object) {
+		if (is_object($object)) {
+			$objectType = get_class($object);
+			$objectRefId = $this->getRefID($object);
+			if (isset($this->associateArray[$objectType][$objectRefId][$object->getID()])) {
+				$res = db_query_params('DELETE FROM fusionforge_object_assiociation WHERE from_id = $1 AND from_object_type = $2
+								AND to_object_type = $3 AND to_id = $4',
+							array($objectType, $object->getID(), $this->getID(), get_class($this)));
+				if ($res) {
+					unset($this->associateArray[$objectType][$objectRefId][$object->getID()]);
+					return true;
+				} else {
+					$this->setError(_('Cannot delete association')._(': ').db_error());
+				}
+			} else {
+				$this->setError(_('Association does not existing'));
+			}
+		} else {
+			$this->setError(_('Cannot remove association to a non-object'));
+		}
+		return false;
+	}
+
+	function removeAllAssociations() {
+		$res = db_query_params('DELETE FROM fusionforge_object_assiociation WHERE (from_id = $1 AND from_object_type = $2)
+											OR (to_id = $1 AND to_object_type $ 4)',
+					array($this->getID(), get_class(), $this->getID(), get_class()));
+		if ($res) {
+			$this->associateToArray = array();
+			$this->associateFromArray = array();
+			return true;
+		} else {
+			$this->setError(_('Unable to remove all associations')._(': ').db_error());
+		}
+		return false;
+	}
+
+	function showAssociations($url = false) {
+		global $HTML;
+		$displayHeader = false;
+		if (count($this->getAssociatedTo()) > 0) {
+			foreach ($this->getAssociatedTo() as $objectType => $objectRefIds) {
+				foreach ($objectRefIds as $objectRefId => $objectIds) {
+					if ($this->checkPermWrapper($objectType, $objectRefId)) {
+						if (!$displayHeader) {
+							$tabletop = array('', _('Associated Object'), _('Associated Object ID'));
+							$classth = array('', '', '');
+							if ($url !== false) {
+								echo html_e('p', array(), _('Remove all association action'));
+								$tabletop[] = _('Actions');
+								$classth[] = 'unsortable';
+							}
+							echo $HTML->listTableTop($tabletop, array(), 'sortable', 'sortable_association', $classth);
+							$displayHeader = true;
+						}
+						foreach ($objectIds as $objectId) {
+							$object = $this->getRefObject($objectId, $objectRefId, $objectType);
+							$cells = array();
+							$cells[][] = _('To');
+							$cells[][] = $objectType;
+							$cells[][] = util_make_link($object->getPermalink(), $objectId);
+							if ($url !== false) {
+								$cells[][] = _('Remove action');
+							} else {
+								$cells[][] = '';
+							}
+							echo $HTML->multiTableRow(array(), $cells);
+						}
+					}
+				}
+			}
+		}
+		if (count($this->getAssociatedFrom()) > 0) {
+			foreach ($this->getAssociatedFrom() as $objectType => $objectRefIds) {
+				foreach ($objectRefIds as $objectRefId => $objectIds) {
+					if ($this->checkPermWrapper($objectType, $objectRefId)) {
+						if (!$displayHeader) {
+							$tabletop = array('', _('Associated Object'), _('Associated Object ID'));
+							$classth = array('', '', '');
+							if ($url !== false) {
+								echo html_e('p', array(), _('Remove all association action'));
+								$tabletop[] = _('Actions');
+								$classth[] = 'unsortable';
+							}
+							echo $HTML->listTableTop($tabletop, array(), 'sortable', 'sortable_association', $classth);
+							$displayHeader = true;
+						}
+						foreach ($objectIds as $objectId) {
+							$object = $this->getRefObject($objectId, $objectRefId, $objectType);
+							$cells = array();
+							$cells[][] = _('From');
+							$cells[][] = $objectType;
+							$cells[][] = util_make_link($object->getPermalink(), $objectId);
+							if ($url !== false) {
+								$cells[][] = _('Remove action');
+							} else {
+								$cells[][] = '';
+							}
+							echo $HTML->multiTableRow(array(), $cells);
+						}
+					}
+				}
+			}
+		}
+		if ($displayHeader) {
+			echo $HTML->listTableBottom();
+		} else {
+			echo $HTML->information(_('No associated object.'));
+		}
+	}
+
+	function showAddAssociations($url = false) {
+		global $HTML;
+		echo _('Add new associate object')._(':');
+		if ($url !== false) {
+			echo $HTML->openForm(array('action' => $url, 'method' => 'post'));
+		}
+		echo html_e('input', array('type' => 'text', 'value' => '', 'name' => 'newobjectsassociation', 'title' => _('Use standard reference such #nnn, Dnnn, to add object association. Comma separeted')));
+		if ($url !== false) {
+			echo html_e('input', array('type' => 'submit', 'value' => _('Add')));
+			echo $HTML->closeForm();
+		}
+	}
+}
diff --git a/src/common/include/utils_crossref.php b/src/common/include/utils_crossref.php
index 6e37c67..b338706 100644
--- a/src/common/include/utils_crossref.php
+++ b/src/common/include/utils_crossref.php
@@ -23,6 +23,7 @@
  */
 
 require_once $gfcommon.'docman/Document.class.php';
+require_once $gfcommon.'tracker/Artifact.class.php';
 
 function util_gen_cross_ref ($text, $group_id) {
 
@@ -53,16 +54,11 @@ function _page2url($group_id,$page) {
 
 function _artifactid2url ($id, $mode='') {
 	$text = '[#'.$id.']';
-	$res = db_query_params ('SELECT group_id, artifact.group_artifact_id, summary, status_id
-			FROM artifact, artifact_group_list
-			WHERE artifact_id=$1
-			AND artifact.group_artifact_id=artifact_group_list.group_artifact_id',
-				array ($id)) ;
-	if (db_numrows($res) == 1) {
-		$row = db_fetch_array($res);
-		$url = '/tracker/?func=detail&aid='.$id.'&group_id='.$row['group_id'].'&atid='.$row['group_artifact_id'];
-		$arg['title'] = util_html_secure($row['summary']);
-		if ($row['status_id'] == 2) {
+	$artifactObject = artifact_get_object($id);
+	if ($artifactObject && is_object($artifactObject) && !$artifactObject->isError()) {
+		$arg['title'] = util_html_secure($artifactObject->getSummary());
+		$url = $artifactObject->getPermalink();
+		if ($artifactObject->getStatusID() == 2) {
 			$arg['class'] = 'artifact_closed';
 		}
 		if ($mode == 'title') {
@@ -121,8 +117,7 @@ function _documentid2url($id, $group_id) {
 	$text = '[D'.$id.']';
 	$d = document_get_object($id, $group_id);
 	if ($d && is_object($d) && !$d->isError()) {
-		$view = (($d->getStateID() != 2) ? 'listfile' : 'listtrashfile');
-		$url = '/docman/?group_id='.$group_id.'&view='.$view.'&dirid='.$d->getDocGroupID().'&filedetailid='.$d->getID();
+		$url = $d->getPermalink();
 		$arg['title'] = $d->getName().' ['.$d->getFileName().']';
 		return util_make_link($url, $text, $arg);
 	}
diff --git a/src/common/tracker/Artifact.class.php b/src/common/tracker/Artifact.class.php
index 990c4f6..85c50f6 100644
--- a/src/common/tracker/Artifact.class.php
+++ b/src/common/tracker/Artifact.class.php
@@ -7,7 +7,7 @@
  * Copyright 2009, Roland Mas
  * Copyright (C) 2009-2013 Alain Peyrat, Alcatel-Lucent
  * Copyright 2012, Thorsten “mirabilos” Glaser <t.glaser at tarent.de>
- * Copyright 2014-2015, Franck Villaume - TrivialDev
+ * Copyright 2014-2016, Franck Villaume - TrivialDev
  * Copyright 2016, Stéphane-Eymeric Bredthauer - TrivialDev
  *
  * This file is part of FusionForge. FusionForge is free software;
@@ -26,7 +26,7 @@
  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  */
 
-/*
+/**
  * Standard Alcatel-Lucent disclaimer for contributing to open source
  *
  * "The Artifact ("Contribution") has not been tested and/or
@@ -47,7 +47,7 @@
  * TOGETHER WITH THE SOFTWARE TO WHICH THE CONTRIBUTION RELATES OR ON A STAND
  * ALONE BASIS."
  */
-require_once $gfcommon.'include/FFError.class.php';
+require_once $gfcommon.'include/FFObject.class.php';
 require_once $gfcommon.'tracker/ArtifactMessage.class.php';
 require_once $gfcommon.'tracker/ArtifactExtraField.class.php';
 require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
@@ -85,7 +85,7 @@ function &artifact_get_object($artifact_id,$data=false) {
 	return $ARTIFACT_OBJ["_".$artifact_id."_"];
 }
 
-class Artifact extends FFError {
+class Artifact extends FFObject {
 
 	/**
 	 * Resource ID.
@@ -141,7 +141,11 @@ class Artifact extends FFError {
 	 *						ONLY OPTIONAL WHEN YOU PLAN TO IMMEDIATELY CALL ->create()
 	 */
 	function __construct(&$ArtifactType, $data=false) {
-		parent::__construct();
+		if (is_int($data)) {
+			parent::__construct($data, get_class());
+		} else {
+			parent::__construct();
+		}
 
 		$this->ArtifactType =& $ArtifactType;
 
@@ -573,6 +577,13 @@ class Artifact extends FFError {
 			return false;
 		}
 
+		if (!$this->removeAllAssociations()) {
+			// error message already set by FFObject class.
+			db_rollback();
+			ArtifactStorage::instance()->rollback();
+			return false;
+		}
+
 		if ($this->getStatusID() == 1) {
 			$res = db_query_params ('UPDATE artifact_counts_agg SET count=count-1,open_count=open_count-1
 				WHERE group_artifact_id=$1',
@@ -2018,6 +2029,10 @@ class Artifact extends FFError {
 		}
 		return false;
 	}
+
+	function getPermalink() {
+		return '/tracker/a_follow.php/'.$this->getID();
+	}
 }
 
 class ArtifactComparator {
diff --git a/src/common/tracker/actions/detail.php b/src/common/tracker/actions/detail.php
index 6b0d479..6a833d6 100644
--- a/src/common/tracker/actions/detail.php
+++ b/src/common/tracker/actions/detail.php
@@ -165,6 +165,9 @@ foreach ($pluginsListeners as $pluginsListener) {
 	<?php if ($ah->hasRelations()) { ?>
 	<li><a href="#tabber-relations"><?php echo _('Relations'); ?></a></li>
 	<?php } ?>
+	<?php if (forge_get_config('use_object_associations')) { ?>
+	<li><a href="#tabber-object-associations"><?php echo _('Associations'); ?></a></li>
+	<?php } ?>
 	</ul>
 	<div id="tabber-comments" class="tabbertab">
 		<?php echo $HTML->listTableTop();
@@ -236,6 +239,13 @@ if ($group->usesPM()) {
 		<?php $ah->showHistory(); ?>
 	</div>
 	<?php $ah->showRelations(); ?>
+	<?php if (forge_get_config('use_object_associations')) { ?>
+	<div id="tabber-object-associations" class="tabbertab">
+		<?php $ah->showAssociations(); ?>
+		<?php if (forge_check_perm ('tracker',$ath->getID(),'submit')) {
+			$ah->showAddAssociations(); } ?>
+	</div>
+	<?php } ?>
 </div>
 <?php if (session_loggedin()) {
 	echo $HTML->listTableTop(); ?>
diff --git a/src/common/tracker/actions/mod-limited.php b/src/common/tracker/actions/mod-limited.php
index 31cac99..ac19fa4 100644
--- a/src/common/tracker/actions/mod-limited.php
+++ b/src/common/tracker/actions/mod-limited.php
@@ -193,6 +193,9 @@ foreach ($pluginsListeners as $pluginsListener) {
 	<?php if ($ah->hasRelations()) { ?>
 	<li><a href="#tabber-relations"><?php echo _('Relations'); ?></a></li>
 	<?php } ?>
+	<?php if (forge_get_config('use_object_associations')) { ?>
+	<li><a href="#tabber-object-associations"><?php echo _('Associations'); ?></a></li>
+	<?php } ?>
 	</ul>
 	<div id="tabber-comments" class="tabbertab">
 	<?php echo $HTML->listTableTop(); ?>
@@ -252,6 +255,13 @@ if ($group->usesPM()) {
 	<?php $ah->showHistory(); ?>
 </div>
 <?php $ah->showRelations(); ?>
+<?php if (forge_get_config('use_object_associations')) { ?>
+<div id="tabber-object-associations" class="tabbertab">
+	<?php $ah->showAssociations(); ?>
+	<?php if (forge_check_perm ('tracker',$ath->getID(),'submit')) {
+		$ah->showAddAssociations(); } ?>
+</div>
+<?php } ?>
 </div>
 <?php if (session_loggedin()) {
 	echo $HTML->listTableTop(); ?>
diff --git a/src/common/tracker/actions/mod.php b/src/common/tracker/actions/mod.php
index a65caae..c67009d 100644
--- a/src/common/tracker/actions/mod.php
+++ b/src/common/tracker/actions/mod.php
@@ -232,6 +232,9 @@ foreach ($pluginsListeners as $pluginsListener) {
 	<?php if ($ah->hasRelations()) { ?>
 	<li><a href="#tabber-relations"><?php echo _('Relations'); ?></a></li>
 	<?php } ?>
+	<?php if (forge_get_config('use_object_associations')) { ?>
+	<li><a href="#tabber-object-associations"><?php echo _('Associations'); ?></a></li>
+	<?php } ?>
 	</ul>
 <div id="tabber-comments" class="tabbertab">
 <?php echo $HTML->listTableTop(); ?>
@@ -312,6 +315,13 @@ if ($group->usesPM()) {
 	<?php $ah->showHistory(); ?>
 </div>
 	<?php $ah->showRelations(); ?>
+	<?php if (forge_get_config('use_object_associations')) { ?>
+	<div id="tabber-object-associations" class="tabbertab">
+		<?php $ah->showAssociations(); ?>
+		<?php if (forge_check_perm ('tracker',$ath->getID(),'submit')) {
+			$ah->showAddAssociations(); } ?>
+	</div>
+	<?php } ?>
 </div>
 <?php if (session_loggedin()) {
 	echo $HTML->listTableTop(); ?>
diff --git a/src/common/tracker/actions/tracker.php b/src/common/tracker/actions/tracker.php
index 98c6839..4b0c758 100644
--- a/src/common/tracker/actions/tracker.php
+++ b/src/common/tracker/actions/tracker.php
@@ -237,6 +237,7 @@ switch (getStringFromRequest('func')) {
 		$extra_fields = getStringFromRequest('extra_fields');
 		$user_email = getStringFromRequest('user_email', false);
 		$was_error = false;
+		$newobjectsassociation = getStringFromRequest('newobjectsassociation', false);
 
 		/*
 			Technicians can modify limited fields - to be certain
@@ -367,6 +368,16 @@ switch (getStringFromRequest('func')) {
 					}
 				}
 
+				// Admin, Techs and Submitter can associate object
+				if ($newobjectsassociation) {
+					if (!$ah->addAssociations($newobjectsassociation)) {
+						$error_msg .= '<br />'._('Associate Object: Error')._(': ').$ah->getErrorMessage();
+						$was_error = true;
+					} else {
+						$feedback .= '<br />'._('Associate Object: Successful');
+					}
+				}
+
 				// Admin and Techs can delete files.
 				if (forge_check_perm ('tracker', $ath->getID(), 'tech')
 						|| forge_check_perm ('tracker', $ath->getID(), 'manager')) {
diff --git a/src/db/20161017-fusionforge_object_association.sql b/src/db/20161017-fusionforge_object_association.sql
new file mode 100644
index 0000000..ca4738b
--- /dev/null
+++ b/src/db/20161017-fusionforge_object_association.sql
@@ -0,0 +1,8 @@
+CREATE TABLE fusionforge_object_assiociation (
+    from_id          integer NOT NULL,
+    from_object_type varchar(50) NOT NULL,
+    from_ref_id      integer NOT NULL,
+    to_id            integer NOT NULL,
+    to_object_type   varchar(50) NOT NULL,
+    to_ref_id        integer NOT NULL
+);
diff --git a/src/etc/config.ini.d/defaults.ini b/src/etc/config.ini.d/defaults.ini
index d16da3e..11f44b0 100644
--- a/src/etc/config.ini.d/defaults.ini
+++ b/src/etc/config.ini.d/defaults.ini
@@ -93,6 +93,7 @@ use_quicknav_default = yes
 use_home = yes
 use_my = yes
 check_password_strength = no
+use_object_associations = yes
 
 scm_single_host = yes
 system_user=fusionforge
diff --git a/src/www/docman/d_follow.php b/src/www/docman/d_follow.php
new file mode 100644
index 0000000..c4594de
--- /dev/null
+++ b/src/www/docman/d_follow.php
@@ -0,0 +1,45 @@
+<?php
+/**
+ * Document UUID implementation for FusionForge
+ *
+ * Copyright © 2010
+ *	Thorsten “mirabilos” Glaser <t.glaser at tarent.de>
+ * All rights reserved.
+ * Copyright 2016, Franck Villaume - TrivialDev
+ *
+ * This file is part of FusionForge. FusionForge is free software;
+ * you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * FusionForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FusionForge; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *-
+ * Follow up to the task information page by UUID (project_task_id)
+ * via a redirection.
+ */
+
+require_once '../env.inc.php';
+require_once $gfcommon.'include/pre.php';
+require_once $gfcommon.'document/Document.class.php';
+
+$docid = getIntFromRequest('docid');
+if (!$docid) {
+	$docid = util_path_info_last_numeric_component();
+}
+if (!$docid) {
+    exit_missing_param('',array(_('docid')), 'docman');
+}
+
+$doc = document_get_object($docid);
+if ($doc && is_object($doc) && !$doc->isError()) {
+	session_redirect('/docman/?view='.$doc->getView().'&group_id='.$doc->Group->getID().'&dirid='.$doc->getDocGroupID().'&filedetailid='.$docid);
+}
+exit_error(_('No Document with ID')._(': ').$docid, 'docman');
diff --git a/src/www/tracker/a_follow.php b/src/www/tracker/a_follow.php
new file mode 100644
index 0000000..6eb4e9d
--- /dev/null
+++ b/src/www/tracker/a_follow.php
@@ -0,0 +1,46 @@
+<?php
+/**
+ * Artifact UUID implementation for FusionForge
+ *
+ * Copyright © 2010
+ *	Thorsten “mirabilos” Glaser <t.glaser at tarent.de>
+ * All rights reserved.
+ * Copyright 2016, Franck Villaume - TrivialDev
+ *
+ * This file is part of FusionForge. FusionForge is free software;
+ * you can redistribute it and/or modify it under the terms of the
+ * GNU General Public License as published by the Free Software
+ * Foundation; either version 2 of the License, or (at your option)
+ * any later version.
+ *
+ * FusionForge is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with FusionForge; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *-
+ * Follow up to the task information page by UUID (project_task_id)
+ * via a redirection.
+ */
+
+require_once '../env.inc.php';
+require_once $gfcommon.'include/pre.php';
+require_once $gfcommon.'tracker/Artifact.class.php';
+
+$aid = getIntFromRequest('aid');
+if (!$aid) {
+	$aid = util_path_info_last_numeric_component();
+}
+if (!$aid) {
+	exit_missing_param('',array(_('aid')), 'tracker');
+}
+
+$at = artifact_get_object($aid);
+
+if ($at && is_object($at) && !$at->isError()) {
+	session_redirect('/tracker/?func=detail&atid='.$at->ArtifactType->getID().'&aid='.$aid.'&group_id='.$at->ArtifactType->Group->getID());
+}
+exit_error(_('No Artifact with ID')._(': ').$aid, 'tracker');

https://scm.fusionforge.org/anonscm/gitweb/?p=fusionforge/fusionforge.git;a=commitdiff;h=d01917cea8b9d1b62e5af10b5beec14a9cd6d2ea

commit d01917cea8b9d1b62e5af10b5beec14a9cd6d2ea
Author: Franck Villaume <franck.villaume at trivialdev.com>
Date:   Sat Oct 22 14:29:10 2016 +0200

    space vs. tab

diff --git a/src/www/pm/t_follow.php b/src/www/pm/t_follow.php
index bcf1f76..09fca3a 100644
--- a/src/www/pm/t_follow.php
+++ b/src/www/pm/t_follow.php
@@ -30,16 +30,17 @@ require_once $gfcommon.'include/pre.php';
 require_once $gfcommon.'pm/ProjectTaskSqlQueries.php';
 
 $tid = getIntFromRequest('tid');
-if (!$tid)
+if (!$tid) {
 	$tid = util_path_info_last_numeric_component();
+}
 if (!$tid) {
-    exit_missing_param('',array(_('TID')),'pm');
+	exit_missing_param('',array(_('TID')),'pm');
 }
 
 $tinfo = getGroupProjectIdGroupId($tid);
 
 if (!$tinfo) {
-    exit_error(_('No Task with ID')._(': ').$tid,'pm');
+	exit_error(_('No Task with ID')._(': ').$tid,'pm');
 }
 
 session_redirect('/pm/task.php?func=detailtask&project_task_id='.$tinfo['project_task_id'].'&group_id='.$tinfo['group_id'].'&group_project_id='.$tinfo['group_project_id']);

-----------------------------------------------------------------------

Summary of changes:
 src/CHANGES                                        |   1 +
 src/common/docman/Document.class.php               |  34 +-
 src/common/include/FFObject.class.php              | 345 +++++++++++++++++++++
 src/common/include/utils_crossref.php              |  19 +-
 src/common/tracker/Artifact.class.php              |  25 +-
 src/common/tracker/actions/detail.php              |  10 +
 src/common/tracker/actions/mod-limited.php         |  10 +
 src/common/tracker/actions/mod.php                 |  10 +
 src/common/tracker/actions/tracker.php             |  11 +
 src/db/20161017-fusionforge_object_association.sql |   8 +
 src/etc/config.ini.d/defaults.ini                  |   1 +
 src/www/{pm/t_follow.php => docman/d_follow.php}   |  26 +-
 src/www/pm/t_follow.php                            |   7 +-
 src/www/{pm/t_follow.php => tracker/a_follow.php}  |  25 +-
 14 files changed, 481 insertions(+), 51 deletions(-)
 create mode 100644 src/common/include/FFObject.class.php
 create mode 100644 src/db/20161017-fusionforge_object_association.sql
 copy src/www/{pm/t_follow.php => docman/d_follow.php} (62%)
 copy src/www/{pm/t_follow.php => tracker/a_follow.php} (63%)


hooks/post-receive
-- 
FusionForge



More information about the Fusionforge-commits mailing list