[Fusionforge-commits] r7687 - in trunk: gforge gforge/common/include gforge/common/tracker gforge/db gforge/www gforge/www/account gforge/www/include gforge/www/js gforge/www/people/admin gforge/www/themes/gforge gforge/www/themes/gforge/images gforge/www/themes/gforge/images/ic gforge/www/tracker gforge/www/tracker/admin gforge/www/tracker/include gforge/www/tracker/reporting tests

Alain Peyrat aljeux at libremir.placard.fr.eu.org
Mon May 18 23:33:45 CEST 2009


Author: aljeux
Date: 2009-05-18 23:33:45 +0200 (Mon, 18 May 2009)
New Revision: 7687

Added:
   trunk/gforge/common/include/utils_crossref.php
   trunk/gforge/common/tracker/ArtifactWorkflow.class.php
   trunk/gforge/db/20090507-add_artifact_workflow.sql
   trunk/gforge/db/20090507-add_element_pos.sql
   trunk/gforge/db/20090507-add_project_query.sql
   trunk/gforge/db/20090507-browse_list.sql
   trunk/gforge/db/20090507-install_workflow.php
   trunk/gforge/www/js/
   trunk/gforge/www/js/common.js
   trunk/gforge/www/js/sortable.js
   trunk/gforge/www/themes/gforge/images/ic/acl_roles20.png
   trunk/gforge/www/themes/gforge/images/ic/btn_down.png
   trunk/gforge/www/themes/gforge/images/ic/btn_up.png
   trunk/gforge/www/themes/gforge/images/spacer.gif
   trunk/gforge/www/tracker/admin/form-customizelist.php
   trunk/gforge/www/tracker/admin/form-workflow.php
   trunk/gforge/www/tracker/admin/form-workflow_roles.php
Modified:
   trunk/gforge/CHANGES
   trunk/gforge/common/tracker/Artifact.class.php
   trunk/gforge/common/tracker/ArtifactExtraField.class.php
   trunk/gforge/common/tracker/ArtifactExtraFieldElement.class.php
   trunk/gforge/common/tracker/ArtifactFactory.class.php
   trunk/gforge/common/tracker/ArtifactQuery.class.php
   trunk/gforge/common/tracker/ArtifactType.class.php
   trunk/gforge/common/tracker/ArtifactsForUser.class.php
   trunk/gforge/db/20070924-artifact-perm.sql
   trunk/gforge/db/20070924-forum-perm.sql
   trunk/gforge/db/20070924-project-perm.sql
   trunk/gforge/db/20090327_create_table_project_tags.sql
   trunk/gforge/db/gforge.sql
   trunk/gforge/db/upgrade-db.php
   trunk/gforge/www/account/index.php
   trunk/gforge/www/include/Layout.class.php
   trunk/gforge/www/include/html.php
   trunk/gforge/www/people/admin/index.php
   trunk/gforge/www/themes/gforge/Theme.class.php
   trunk/gforge/www/tracker/add.php
   trunk/gforge/www/tracker/admin/form-addcanned.php
   trunk/gforge/www/tracker/admin/form-addextrafield.php
   trunk/gforge/www/tracker/admin/form-addextrafieldoption.php
   trunk/gforge/www/tracker/admin/form-extrafieldcopy.php
   trunk/gforge/www/tracker/admin/form-updatecanned.php
   trunk/gforge/www/tracker/admin/form-updateextrafield.php
   trunk/gforge/www/tracker/admin/form-updatetracker.php
   trunk/gforge/www/tracker/admin/ind.php
   trunk/gforge/www/tracker/admin/index.php
   trunk/gforge/www/tracker/admin/updates.php
   trunk/gforge/www/tracker/browse.php
   trunk/gforge/www/tracker/detail.php
   trunk/gforge/www/tracker/downloadcsv.php
   trunk/gforge/www/tracker/include/ArtifactHtml.class.php
   trunk/gforge/www/tracker/include/ArtifactTypeHtml.class.php
   trunk/gforge/www/tracker/ind.php
   trunk/gforge/www/tracker/mod-limited.php
   trunk/gforge/www/tracker/mod.php
   trunk/gforge/www/tracker/query.php
   trunk/gforge/www/tracker/reporting/index.php
   trunk/gforge/www/tracker/reporting/trackeract_graph.php
   trunk/gforge/www/tracker/taskmgr.php
   trunk/gforge/www/tracker/tracker.php
   trunk/tests/TarCentos52Tests.php
Log:
Massive improvements on trackers (contributed by Alcatel-Lucent), see CHANGES for more

Modified: trunk/gforge/CHANGES
===================================================================
--- trunk/gforge/CHANGES	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/CHANGES	2009-05-18 21:33:45 UTC (rev 7687)
@@ -1,4 +1,4 @@
-Not released yet:
+FusionForge-4.8: (Not released yet)
 * New projectlabels plugin, to tag projects with snippets of
   user-defined HTML (developed for/sponsored by Adullact)
 * New extratabs plugin, to add tabs with links to arbitrary URLs
@@ -12,6 +12,22 @@
   FRS (AdaCore again)
 * New command-line scripts to inject users, groups and file releases
   into the database from text files (AdaCore again)
+* Trackers: The list of fields displayed when browsing the list of artifacts
+  can now be defined (Alcatel-Lucent)
+* Trackers: The description field can now be updated/corrected (Alcatel-Lucent)
+* Trackers: It is now possible to force a custom field to be mandatory (Alcatel-Lucent)
+* Trackers: The values for 'select box' custom fields can now be reordered (Alcatel-Lucent)
+* Trackers: A workflow on the status field (when overwritten) can be set. Allowed
+  transition between values can be defined and allowed per roles (Alcatel-Lucent)
+* Trackers: A new type of extra field has been added: Relation between artifact. 
+  This type allow to create a relation between artifacts. Backwards relation can
+  also be visible (Alcatel-Lucent)
+* Trackers: Dynamic links added, expressions like [#NNN],[TNNN] are now rendered as
+  links for description and comments (Alcatel-Lucent).
+* Trackers: Search improved to allow searching in text fields (Alcatel-Lucent).
+* Trackers: New system to share a search query. Shared queries can be represented 
+  as an URL or bookmarked. It is also possible to define one query as the default
+  one (Alcatel-Lucent) 
 
 FusionForge-4.8:
 * New classification by tags (aka tag cloud)

Added: trunk/gforge/common/include/utils_crossref.php
===================================================================
--- trunk/gforge/common/include/utils_crossref.php	                        (rev 0)
+++ trunk/gforge/common/include/utils_crossref.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,107 @@
+<?php
+/**
+ * utils_crossref.php - Misc utils common to all aspects of the site
+ *
+ * Copyright 1999-2001 (c) Alcatel-Lucent
+ *
+ * @version   $Id: utils.php 5732 2006-09-30 21:04:41Z marcelo $
+ *
+ * This file is part of GForge.
+ *
+ * GForge 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.
+ *
+ * GForge 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 GForge; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  US
+ */
+
+
+function util_gen_cross_ref ($text, $group_id) {
+
+	// Some important information.
+	$prj = group_getunixname ($group_id);
+	
+	// Handle URL in links, replace them with hyperlinks.
+	$text = util_make_links($text);
+	
+	// Handle gforge [#nnn] Syntax => links to tracker.
+	$text = preg_replace('/\[\#(\d+)\]/e', "_artifactid2url('\\1')", $text);
+	
+	// Handle gforge [Tnnn] Syntax => links to task.
+	$text = preg_replace('/\[\T(\d+)\]/e', "_taskid2url('\\1')", $text);
+	
+	// Handle [wiki:<pagename>] syntax
+	$text = preg_replace('/\[wiki:(\S+)\]/', "<a href=\"/wiki/g/$prj/\\1\">\\1</a>", $text);
+	
+	// Handle [forum:<thread_id>] Syntax => links to forum.
+	$text = preg_replace('/\[forum:(\d+)\]/e', "_forumid2url('\\1')", $text);
+	
+	return $text;
+}
+
+function _artifactid2url ($id, $mode='') {
+	$sql = "SELECT group_id, artifact.group_artifact_id, summary, status_id
+			FROM artifact, artifact_group_list
+			WHERE artifact_id=$id
+			AND artifact.group_artifact_id=artifact_group_list.group_artifact_id";
+	$text = '[#'.$id.']';
+	$res = db_query($sql);
+	if (db_numrows($res) == 1) {
+		$row = db_fetch_array($res);
+		$url = '/tracker/?func=detail&amp;aid='.$id.'&amp;group_id='.$row['group_id'].'&amp;atid='.$row['group_artifact_id'];
+		$arg = 'title="'.$row['summary'].'"' ;
+		if ($row['status_id'] == 2) {
+			$arg .= 'class="artifact_closed"'; 
+		}
+		if ($mode == 'title') {
+			return '<a href="'.$url.'" '.$arg.'>'.$text.'</a> <a href="'.$url.'">'.$row['summary'].'</a><br />';
+		} else {
+			return '<a href="'.$url.'" '.$arg.'>'.$text.'</a>';
+		}
+	}
+	return $text;
+}
+
+function _taskid2url ($id) {
+	$sql = "SELECT group_id, project_task.group_project_id, summary, status_id
+			FROM project_task, project_group_list
+			WHERE project_task_id=$id
+			AND project_task.group_project_id=project_group_list.group_project_id";
+	$text = '[T'.$id.']';
+	$res = db_query($sql);
+	if (db_numrows($res) == 1) {
+		$row = db_fetch_array($res);
+		$url = '/pm/task.php?func=detailtask&amp;project_task_id='.$id.'&amp;group_id='.$row['group_id'].'&amp;group_project_id='.$row['group_project_id'];
+		$arg = 'title="'.$row['summary'].'"' ;
+		if ($row['status_id'] == 2) {
+			$arg .= 'class="task_closed"'; 
+		}
+		return '<a href="'.$url.'" '.$arg.'>'.$text.'</a>';
+	}
+	return $text;
+}
+
+function _forumid2url ($id) {
+	$sql = "SELECT group_id, forum.group_forum_id, subject
+			FROM forum, forum_group_list
+			WHERE msg_id=$id
+			AND forum.group_forum_id=forum_group_list.group_forum_id";
+	$text = '[forum:'.$id.']';
+	$res = db_query($sql);
+	if (db_numrows($res) == 1) {
+		$row = db_fetch_array($res);
+		$url = '/forum/message.php?msg_id='.$id.'&amp;group_id='.$row['group_id'];
+		$arg = 'title="'.$row['subject'].'"' ;
+		return '<a href="'.$url.'" '.$arg.'>'.$text.'</a>';
+	}
+	return $text;
+}
+?>

Modified: trunk/gforge/common/tracker/Artifact.class.php
===================================================================
--- trunk/gforge/common/tracker/Artifact.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/Artifact.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -27,6 +27,7 @@
 require_once $gfcommon.'include/Error.class.php';
 require_once $gfcommon.'tracker/ArtifactMessage.class.php';
 require_once $gfcommon.'tracker/ArtifactExtraField.class.php';
+require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
 
 // This string is used when sending the notification mail for identifying the
 // user response
@@ -175,10 +176,13 @@
 			//
 			//	Only admins can post/modify private artifacts
 			//
-			if (!$this->ArtifactType->userIsAdmin()) {
-				$this->setError(_('Artifact: Only Artifact Admins Can Modify Private ArtifactTypes'));
-				return false;
-			}
+
+//
+// ape: Disabled, private means only restricted to members. So, no special rules #2503.
+//			if (!$this->ArtifactType->userIsAdmin()) {
+//				$this->setError($Language->getText('tracker_artifact','error_admin_modify'));
+//				return false;
+//			}
 		}
 
 		//
@@ -657,7 +661,9 @@
 	 */
 	function &getFiles() {
 		if (!isset($this->files)) {
-			$res = db_query_params ('SELECT * FROM artifact_file_user_vw WHERE artifact_id=$1',
+			$res = db_query_params ('SELECT id,artifact_id,description,filename,filesize," .
+					"filetype,adddate,submitted_by,user_name,realname
+					 FROM artifact_file_user_vw WHERE artifact_id=$1',
 						array ($this->getID())) ;
 			$rows=db_numrows($res);
 			if ($rows > 0) {
@@ -769,10 +775,12 @@
 	 *	@param	string	Attaching another comment.
 	 *	@param	int		Allows you to move an artifact to another type.
 	 *	@param	array	Array of extra fields like: array(15=>'foobar',22=>'1');
+	 *  @param  string  The description.
 	 *	@return	boolean	success.
 	 */
 	function update($priority,$status_id,
-		$assigned_to,$summary,$canned_response,$details,$new_artifact_type_id,$extra_fields=array()) {
+		$assigned_to,$summary,$canned_response,$details,$new_artifact_type_id,
+		$extra_fields=array(), $description='') {
 
 		/*
 			Field-level permission checking
@@ -783,6 +791,7 @@
 			//everyone else cannot modify these fields
 			$priority=$this->getPriority();
 			$summary=addslashes($this->getSummary());
+			$description=addslashes($this->getDetails());
 			$canned_response=100;
 			$new_artifact_type_id=$this->ArtifactType->getID();
 			$assigned_to=$this->getAssignedTo();
@@ -815,6 +824,23 @@
 		}
 
 
+		// Check that assigned_to is member of the project.
+		if ($assigned_to != 100) {
+			$res = $this->ArtifactType->getTechnicians();
+			$arr =& util_result_column_to_array($res,0);
+			$found = false;
+			foreach (array_values($arr) as $r)
+			{
+				if ($r == $assigned_to) {
+					$found = true;
+				}
+			}
+			if (!$found) {
+				$this->setError("Invalid assigned_to (not member of the project)");
+				return false;
+			}
+		}
+
 		// Array to record which properties were changed
 		$changes = array();
 		$update  = false;
@@ -875,12 +901,17 @@
 			$changes['status'] = 1;
 			$update = true;
 
-			// Reset the close_date if bug is re-opened 
-			// (otherwise stat reports will be wrong).
-			if ($status_id == 1) {
-				$close_date = 0 ;
-				$this->addHistory('close_date',0);
-			}			
+			//
+			//	Enter the timestamp if we are changing to closed
+			//
+			if ($status_id != 1) {
+				$sqlu .= " close_date='".time()."', ";
+			} else {
+				// Reset the close_date if bug is re-opened 
+				// (otherwise stat reports will be wrong).
+				$sqlu .= " close_date='0', ";
+			}
+			$this->addHistory('close_date', $this->getCloseDate());
 		}
 		if ($this->getPriority() != $priority) {
 			$this->addHistory('priority',$this->getPriority());
@@ -893,28 +924,22 @@
 			$changes['assigned_to'] = 1;
 			$update = true;
 		}
-		if ($summary && (addslashes($this->getSummary()) != htmlspecialchars($summary))) {
-			$this->addHistory('summary', addslashes($this->getSummary()));
+		if ($summary && ($this->getSummary() != stripslashes($summary))) {
+			$this->addHistory('summary', $this->getSummary());
 			$changes['summary'] = 1;
 			$update = true;
 		}
-
+ 		if ($description && ($this->getDetails() != stripslashes($description))) {
+ 			$this->addHistory('details', $this->getDetails());
+ 			$changes['details'] = 1;
+ 			$update = true;
+  		}
 		if ($details) {
 			$this->addMessage($details,'',0);
 			$changes['details'] = 1;
 			$send_message=true;
 		}
 
-		//
-		//	Enter the timestamp if we are changing to closed
-		//
-		if ($status_id != 1) {
-			$now = time();
-			$close_date = $now ;
-			$this->addHistory('close_date',$now);
-			$update = true;
-		}
-
 		/*
 			Finally, update the artifact itself
 		*/
@@ -925,14 +950,16 @@
 				priority=$2,
 				assigned_to=$3,
 				summary=$4,
-				close_date=$5,
-				group_artifact_id=$6
+				details=$5,
+				close_date=$6,
+				group_artifact_id=$7
 				WHERE 
-				artifact_id=$7 AND group_artifact_id=$8',
+				artifact_id=$8 AND group_artifact_id=$9',
 						   array ($status_id,
 							  $priority,
 							  $assigned_to,
-							  htmlspecialchars ($summary),
+							  htmlspecialchars($summary),
+							  htmlspecialchars($description),
 							  $close_date,
 							  $new_artifact_type_id,
 							  $this->getID(),
@@ -1033,20 +1060,72 @@
 		$ef = $this->ArtifactType->getExtraFields();
 		$efk=array_keys($ef);
 
+		// If there is a status field, then check against the workflow.
+		for ($i=0; $i<count($efk); $i++) {
+			$efid=$efk[$i];
+			$type=$ef[$efid]['field_type'];
+			if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
+				// Get previous value.
+				$sql =  "SELECT field_data FROM artifact_extra_field_data
+						WHERE artifact_id='".$this->getID()."' AND extra_field_id='".$efid."'";
+				$res = db_query($sql);
+				$old = (db_numrows($res)>0) ? db_result($res,0,'field_data') : 100;
+				if ($old != $extra_fields[$efid]) {
+					$atw = new ArtifactWorkflow($this->ArtifactType, $efid);
+					if (!$atw->checkEvent($old, $extra_fields[$efid])) {
+						$this->setError('Workflow error: You are not authorized to change the Status');
+						return false;
+					}
+				}
+			}
+		}
+		
 		//now we'll update this artifact for each extra field
 		for ($i=0; $i<count($efk); $i++) {
 			$efid=$efk[$i];
 			$type=$ef[$efid]['field_type'];
 
+			// check required fields
+			if ($ef[$efid]['is_required']) {
+				if (!array_key_exists($efid, $extra_fields)) {
+					if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
+						$this->setError('Status Custom Field Must Be Set');
+					}
+					else {
+						$this->setMissingParamsError($ef[$efid]['field_name']);
+					}
+					return false;
+				}
+				else {
+					if (!$extra_fields[$efid]) {
+						if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
+							$this->setError('Status Custom Field Must Be Set');
+						}
+						else {
+							$this->setMissingParamsError($ef[$efid]['field_name']);
+						}
+						return false;
+					}
+					else {
+						if (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT || $type == ARTIFACT_EXTRAFIELDTYPE_RADIO) &&
+							$extra_fields[$efid] == '100') {
+								$this->setMissingParamsError($ef[$efid]['field_name']);
+								return false;
+						}
+						elseif (($type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT || $type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) &&
+								(count($extra_fields[$efid]) == 1 && $extra_fields[$efid][0] == '100')) {
+							$this->setMissingParamsError($ef[$efid]['field_name']);
+							return false;
+						}
+					}
+				}
+			}
 //
 //	Force each field to have some value if it is a numeric field
 //	text fields will just be purged and skipped
 //
 			if (!array_key_exists($efid, $extra_fields) || !$extra_fields[$efid]) {
-				if ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
-					$this->setError('Status Custom Field Must Be Set');
-					return false;
-				} elseif (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_RADIO)) {
+				if (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_RADIO)) {
 					$extra_fields[$efid]='100';
 				} elseif (($type == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX)) {
 					$extra_fields[$efid]=array('100');
@@ -1084,13 +1163,9 @@
 					if (!empty($added_values) || !empty($deleted_values))	{	// there are differences...
 						$field_name = $ef[$efid]['field_name'];
 						$changes["extra_fields"][$efid] = 1;
-						
-						// Do a history entry only for deleted values
-						if (!empty($deleted_values)) {
-							$this->addHistory($field_name, $this->ArtifactType->getElementName($deleted_values));
-						}
-						
-						
+
+						$this->addHistory($field_name, $this->ArtifactType->getElementName(array_reverse($old_values)));
+
 						$resdel = db_query_params ('DELETE FROM artifact_extra_field_data WHERE	artifact_id=$1 AND extra_field_id=$2',
 									   array ($this->getID(),
 										  $efid)) ;
@@ -1107,9 +1182,12 @@
 					$resdel = db_query_params ('DELETE FROM artifact_extra_field_data WHERE	artifact_id=$1 AND extra_field_id=$2',
 								   array ($this->getID(),
 									  $efid)) ;
+
+					// Adding history with previous value.
 					if (($type == ARTIFACT_EXTRAFIELDTYPE_SELECT) || ($type == ARTIFACT_EXTRAFIELDTYPE_RADIO) || ($type == ARTIFACT_EXTRAFIELDTYPE_STATUS)) {
-//don't add history for text fields
 						$this->addHistory($field_name,$this->ArtifactType->getElementName(db_result($resd,0,'field_data')));
+					} else {
+						$this->addHistory($field_name, db_result($resd,0,'field_data'));
 					}
 				}
 			} else {
@@ -1117,7 +1195,39 @@
 //no history for this extra field exists
 
 			}
+
 			//
+			// Some rewrite & consistency checks on the relation type field.
+			//
+			// 1) Convert syntax [#NNN] to NNN
+			// 2) Allow multiple spaces as separator.
+			// 3) Ensure that only integers are given.
+			// 4) Ensure that id corresponds to valid tracker id.
+			//
+			if ($type == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
+				$value = preg_replace('/\[\#(\d+)\]/', "\\1", trim($extra_fields[$efid]));
+				$value = preg_replace('/\\s+/', ' ', $value);
+				$new = '';
+				foreach (explode(' ',$value) as $id) {
+					if (preg_match('/^(\d+)$/', $id)) {
+						// Control that the id is present in the db
+						$sql = "SELECT artifact_id FROM artifact WHERE artifact_id=$id";
+						$res = db_query($sql);
+						if (db_numrows($res) == 1) {
+							$new .= $id.' ';
+						} else {
+							$this->setError('Illegal id '.$id.', it\'s not a valid tracker id for field: '.$ef[$efid]['field_name'].'.'); // @todo: lang
+							return false;
+						}
+					} else {
+						$this->setError('Illegal value '.$id.', only trackers id are allowed for field: '.$ef[$efid]['field_name'].'.'); // @todo: lang
+						return false;
+					}
+				}
+				$extra_fields[$efid] = trim($new);
+			}
+			
+			//
 			//	See if anything was even passed for this extra_field_id
 			//
 			if (!$extra_fields[$efid]) {
@@ -1134,7 +1244,7 @@
 									       $efid,
 									       $extra_fields[$efid][$fin])) ;
 						if (!$res) {
-							$this->setError('Artifact::updateExtraFields:: '.$sql.db_error());
+							$this->setError(db_error());
 							return false;
 						}
 					}
@@ -1146,7 +1256,7 @@
 								       $efid,
 								       htmlspecialchars($extra_fields[$efid]))) ;
 					if (!$res) {
-						$this->setError('Artifact::updateExtraFields:: '.db_error());
+						$this->setError(db_error());
 						return false;
 					}
 				}
@@ -1208,6 +1318,9 @@
 	 *	@return	boolean	success.
 	 */
 	function mailFollowup($type, $more_addresses=false, $changes='') {
+
+		$monitor_ids = array();
+
 		if (!$changes) {
 			$changes=array();
 		}
@@ -1234,7 +1347,7 @@
 					    "&group_id=". $this->ArtifactType->Group->getID()) .
 			"\nOr by replying to this e-mail entering your response between the following markers: ".
 			"\n".ARTIFACT_MAIL_MARKER.
-			"\n(enter your response here)".
+			"\n(enter your response here, only in plain text format)".
 			"\n".ARTIFACT_MAIL_MARKER.
 			"\n\n".
 			$this->marker('status',$changes).
@@ -1391,22 +1504,24 @@
 
 		foreach ($efs as $efid => $ef) {
 			$name = $ef["field_name"];
+			$type = $ef["field_type"];
 			
 			// Get the value according to the type
-			switch ($ef["field_type"]) {
+			switch ($type) {
 				
-			// for these types, the associated value comes straight
-			case ARTIFACT_EXTRAFIELDTYPE_TEXT:
-			case ARTIFACT_EXTRAFIELDTYPE_TEXTAREA:
-				$value = isset($efd[$efid]) ? $efd[$efid]: '';
-				break;
-
-			// the other types have and ID or an array of IDs associated to them
-			default:
-				$value = $this->ArtifactType->getElementName($efd[$efid]);
+				// for these types, the associated value comes straight
+				case ARTIFACT_EXTRAFIELDTYPE_TEXT:
+				case ARTIFACT_EXTRAFIELDTYPE_TEXTAREA:
+				case ARTIFACT_EXTRAFIELDTYPE_RELATION:
+					$value = isset($efd[$efid]) ? $efd[$efid]: '';
+					break;
+	
+				// the other types have and ID or an array of IDs associated to them
+				default:
+					$value = $this->ArtifactType->getElementName($efd[$efid]);
 			}
 			
-			$return[$efid] = array("name" => $name, "value" => $value);
+			$return[$efid] = array("name" => $name, "value" => $value, 'type' => $type);
 		}
 		
 		return $return;

Modified: trunk/gforge/common/tracker/ArtifactExtraField.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactExtraField.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/ArtifactExtraField.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -34,6 +34,7 @@
 define('ARTIFACT_EXTRAFIELDTYPE_TEXTAREA',6);
 define('ARTIFACT_EXTRAFIELDTYPE_STATUS',7);
 //define('ARTIFACT_EXTRAFIELDTYPE_ASSIGNEE',8);
+define('ARTIFACT_EXTRAFIELDTYPE_RELATION',9);
 
 class ArtifactExtraField extends Error {
 
@@ -108,10 +109,33 @@
 			$this->setError(_('a field name is required'));
 			return false;
 		}
+		if (!$field_type) {
+			$this->setError("Type of custom field not selected");
+			return false;			
+		}
 		if (!$this->ArtifactType->userIsAdmin()) {
 			$this->setPermissionDeniedError();
 			return false;
 		}
+		$sql = "SELECT field_name FROM artifact_extra_field_list WHERE field_name='$name' AND group_artifact_id=".$this->ArtifactType->getID();
+		$res = db_query($sql);
+		if (db_numrows($res) > 0) {
+			$this->setError(_('Field name already exists'));
+			return false;
+		}
+		if ($field_type == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
+			if (!$attribute1 || !$attribute2 || $attribute2 < $attribute1) {
+				$this->setError("Invalid size/maxlength for text field");
+				return false;
+			}
+		}
+		if ($field_type == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
+			if (!$attribute1 || !$attribute2) {
+				$this->setError("Invalid rows/cols for textarea field");
+				return false;
+			}
+		}
+		
 		if ($is_required) {
 			$is_required=1;
 		} else {
@@ -148,41 +172,28 @@
 					db_rollback();
 					return false;
 				} else {
-//
-//	Must insert some default statuses for each artifact
-//
-					$reso = db_query_params ('INSERT INTO artifact_extra_field_elements(extra_field_id,element_name,status_id) VALUES ($1,$2,$3)',
-								 array ($id,
-									'Open',
-									1)) ;
-					if (!$reso) {
-						echo db_error();
+					//
+					//	Must insert some default statuses for each artifact
+					//
+					$ao = new ArtifactExtraFieldElement($this);
+					if (!$ao || !is_object($ao)) {
+						$feedback .= 'Unable to create ArtifactExtraFieldElement Object';
+						db_rollback();
+						return false;
 					} else {
-						$resoid=db_insertid($reso,'artifact_extra_field_elements','element_id');
-						db_query_params ('INSERT INTO artifact_extra_field_data(artifact_id,field_data,extra_field_id) 
-							SELECT artifact_id,$1,$2 FROM artifact 
-							WHERE group_artifact_id=$3
-							AND status_id=1',
-								 array ($resoid,
-									$id,
-									$this->ArtifactType->getID())) ;
+						if (!$ao->create('Open', '1')) {
+							$feedback .= _('Error inserting an element').': '.$ao->getErrorMessage();
+							$ao->clearError();
+							db_rollback();
+							return false;
+						}
+						if (!$ao->create('Closed', '2')) {
+							$feedback .= _('Error inserting an element').': '.$ao->getErrorMessage();
+							$ao->clearError();
+							db_rollback();
+							return false;
+						}
 					}
-					$resc = db_query_params ('INSERT INTO artifact_extra_field_elements(extra_field_id,element_name,status_id) VALUES ($1,$2,$3)',
-								 array ($id,
-									'Closed',
-									2)) ;
-					if (!$resc) {
-						echo db_error();
-					} else {
-						$rescid=db_insertid($resc,'artifact_extra_field_elements','element_id');
-						db_query_params ('INSERT INTO artifact_extra_field_data(artifact_id,field_data,extra_field_id) 
-							SELECT artifact_id,$1,$2 FROM artifact 
-							WHERE group_artifact_id=$3
-							AND status_id != 1',
-								 array ($rescid,
-									$id,
-									$this->ArtifactType->getID())) ;
-					}
 				}
 			} elseif (strstr(ARTIFACT_EXTRAFIELD_FILTER_INT,$field_type) !== false) {
 //
@@ -311,7 +322,8 @@
 			4=>_('Text Field'),
 			5=>_('Multi-Select Box'),
 			6=>_('Text Area'),
-			7=>_('Status')
+			7=>_('Status'),
+			9=>_('Relation')
 		);
 	}
 	
@@ -364,6 +376,13 @@
 			$this->setError(_('a field name is required'));
 			return false;
 		}
+		$sql = "SELECT field_name FROM artifact_extra_field_list 
+				WHERE field_name='$name' AND group_artifact_id=".$this->ArtifactType->getID()." AND extra_field_id !='". $this->getID();
+		$res = db_query($sql);
+		if (db_numrows($res) > 0) {
+			$this->setError(_('Field name already exists'));
+			return false;
+		}
 		if ($is_required) {
 			$is_required=1;
 		} else {
@@ -531,6 +550,67 @@
 		// at this point, the alias is valid and unique
 		return $alias;
 	}
+
+	function updateOrder($element_id, $order) {
+		$sql = 'UPDATE artifact_extra_field_elements
+				SET element_pos=' . $order . '
+				WHERE element_id=' . $element_id;
+		$result=db_query($sql);
+		if ($result && db_affected_rows($result) > 0) {
+			return true;
+		}
+		else {
+			$this->setError(db_error());
+			return false;
+		}
+	}
+
+	function reorderValues($element_id, $new_pos) {
+		global $Language;
+
+		$sql = 'SELECT element_id FROM artifact_extra_field_elements WHERE extra_field_id=' .$this->getID() .
+			   ' ORDER BY element_pos ASC, element_id ASC';
+		$res = db_query($sql);
+		$max = db_numrows($res);
+		if ($new_pos < 1 || $new_pos > $max) {
+			$this->setError(_('Out of range value'));
+			return false;
+		}
+		$i = 1;
+		while ($i <= $max) {
+			if ($i == $new_pos) {
+				$data[] = $element_id;
+				$i++;
+			}
+			if (($row = db_fetch_array($res)) && $row['element_id'] != $element_id) {
+				$data[] = $row['element_id'];
+				$i++;
+			}
+		}
+		for ($i = 0; $i < count($data); $i++) {
+			if (! $this->updateOrder($data[$i], $i + 1))
+				return false;
+		}
+
+		return true;
+	}
+
+	function alphaorderValues($element_id) {
+		global $Language;
+
+		$sql = 'SELECT element_id FROM artifact_extra_field_elements WHERE extra_field_id=' .$this->getID() .
+			   ' ORDER BY element_name ASC';
+		$res = db_query($sql);
+		$i = 1;
+		while ($row = db_fetch_array($res)) {
+			if (! $this->updateOrder($row['element_id'], $i))
+				return false;
+			$i++;
+		}
+
+		return true;
+	}
+
 }
 
 // Local Variables:

Modified: trunk/gforge/common/tracker/ArtifactExtraFieldElement.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactExtraFieldElement.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/ArtifactExtraFieldElement.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -24,6 +24,7 @@
  */
 
 require_once $gfcommon.'include/Error.class.php';
+require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
 
 class ArtifactExtraFieldElement extends Error {
 
@@ -129,6 +130,12 @@
 				db_rollback();
 				return false;
 			} else {
+				// If new element belongs to Status custom field, then register the new element in the workflow.
+				if ($this->ArtifactExtraField->getType() == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
+					$atw = new ArtifactWorkflow($this->ArtifactExtraField->ArtifactType, $this->ArtifactExtraField->getID());
+					$atw->addNode($id);
+				}
+				
 				db_commit();
 				return $id;
 			}

Modified: trunk/gforge/common/tracker/ArtifactFactory.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactFactory.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/ArtifactFactory.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -56,6 +56,9 @@
 	var $moddaterange;
 	var $opendaterange;
 	var $closedaterange;
+	
+	var $query_type;		// query, default, custom
+	var $query_id;			// id of the query (when query_type=query)
 
 	/**
 	 *  Constructor.
@@ -113,23 +116,17 @@
 				if no set is passed in, see if a preference was set
 				if no preference or not logged in, use open set
 			*/
+			$this->query_type = '';
 			if (session_loggedin()) {
-				$default_query=$u->getPreference('art_query'.$this->ArtifactType->getID());
-				$this->defaultquery = $default_query;
-				if ($default_query) {
-					$aq = new ArtifactQuery($this->ArtifactType,$default_query);
-					$_extra_fields=$aq->getExtraFields();
-					$order_col=$aq->getSortCol();
-					$sort=$aq->getSortOrd();
-					$_assigned_to=$aq->getAssignee();
-					$_status=$aq->getStatus();
-					$this->moddaterange = $aq->getModDateRange();
-					$this->opendaterange = $aq->getOpenDateRange();
-					$this->closedaterange = $aq->getCloseDateRange();
+				$query_id=$u->getPreference('art_query'.$this->ArtifactType->getID());
+				if ($query_id) {
+					$this->query_type = 'query';
+					$this->query_id = $query_id;
 				} else {
 					$custom_pref=$u->getPreference('art_cust'.$this->ArtifactType->getID());
 					if ($custom_pref) {
 //$_assigned_to.'|'.$_status.'|'.$_order_col.'|'.$_sort_ord.'|'.$_changed.'|'.serialize($_extra_fields);
+						$this->query_type = 'custom';
 						$pref_arr=explode('|',$custom_pref);
 						$_assigned_to=$pref_arr[0];
 						$_status=$pref_arr[1];
@@ -142,19 +139,50 @@
 							$_status=$pref_arr[1];
 						}
 						$set='custom';
-					} else {
-						//default to open
-						$_assigned_to=0;
-						$_status=1;
-						$_changed=0;
 					}
 				}
-			} else {
-				//default to open
+			} elseif (isset($_COOKIE["GFTrackerQuery"])) {
+				$gf_tracker = unserialize($_COOKIE["GFTrackerQuery"]);
+				$query_id = (int)$gf_tracker[$this->ArtifactType->getID()];
+				if ($query_id) { 
+					$this->query_type = 'query';
+					$this->query_id = $query_id;
+				}
+			}
+
+			if (!$this->query_type) {
+				$sql = "SELECT artifact_query_id FROM artifact_query
+					WHERE group_artifact_id='".$this->ArtifactType->getID()."'
+					AND query_type=2";
+				$res = db_query($sql);
+				if (db_numrows($res)>0) {
+					$this->query_type = 'query';
+					$this->query_id = db_result($res, 0, 'artifact_query_id');
+				}
+			}
+
+			if (!$this->query_type) {
+				//default to all opened
+				$this->query_type = 'default';
 				$_assigned_to=0;
 				$_status=1;
 				$_changed=0;
 			}
+
+			if ($this->query_type == 'query') {
+				$aq = new ArtifactQuery($this->ArtifactType, $this->query_id);
+				$_assigned_to=$aq->getAssignee();
+				$_status=$aq->getStatus();
+				$_extra_fields=$aq->getExtraFields();
+				$this->moddaterange = $aq->getModDateRange();
+				$this->opendaterange = $aq->getOpenDateRange();
+				$this->closedaterange = $aq->getCloseDateRange();
+				$this->summary = $aq->getSummary();
+				$this->description = $aq->getDescription();
+				$this->followups = $aq->getFollowups();
+				$order_col=$aq->getSortCol();
+				$sort=$aq->getSortOrd();
+			}
 		}
 
 		//
@@ -172,6 +200,7 @@
 		$_sort_ord = util_ensure_value_in_set ($sort,
 						       array ('ASC', 'DESC')) ;
 		if ($set=='custom') {
+			$this->query_type = 'custom';
 			if (session_loggedin()) {
 				/*
 					if this custom set is different than the stored one, reset preference
@@ -211,7 +240,8 @@
 		$this->sort=$_sort_ord;
 		$this->order_col=$_order_col;
 		$this->status=$_status;
-		if ($_assigned_to != 'Array') {
+		if (gettype($_assigned_to) === 'integer' || 
+			gettype($_assigned_to) === 'string') {
 			$this->assigned_to=$_assigned_to;
 		}
 		$this->extra_fields=$_extra_fields;
@@ -221,7 +251,7 @@
 		if (is_null($max_rows) || $max_rows < 0) {
 			$max_rows=50;
 		}
-		if (isset ($default_query)) {
+		if ($this->query_type == 'query') {
 			$this->max_rows=0;
 		} else {
 			$this->max_rows=$max_rows;
@@ -245,7 +275,10 @@
 	 *	@return	int	
 	 */
 	function getDefaultQuery() {
-		return $this->defaultquery;
+		if ($this->query_type == 'query')
+			return $this->query_id;
+		else
+			return '';
 	}
 	
 	/**
@@ -278,8 +311,18 @@
 				$selectsql .= ', artifact_extra_field_data aefd'.$i;
 				$wheresql .= ' AND aefd'.$i.'.extra_field_id=$'.$paramcount++ ;
 				$params[] = $keys[$i] ;
-				$wheresql .= ' AND aefd'.$i.'.field_data = ANY ($'.$paramcount++ ;
-				$params[] = db_string_array_to_any_clause ($vals[$i]) ;
+
+				// Hack: Determine the type of the element to get the right search query.
+				$sql = "SELECT field_type FROM artifact_extra_field_list WHERE extra_field_id=".$keys[$i];
+				$type = db_result(db_query($sql),0,'field_type');
+				if ($type == 4 or $type == 6) {
+					$search = "LIKE '".addslashes($vals[$i])."'";
+					$wheresql .= ' AND aefd'.$i.'.field_data LIKE $'.$paramcount++ ;
+					$params[] = $vals[$i];
+				} else {
+					$wheresql .= ' AND aefd'.$i.'.field_data = ANY ($'.$paramcount++ ;
+					$params[] = db_string_array_to_any_clause ($vals[$i]) ;
+				}
 				$wheresql .= ') AND aefd'.$i.'.artifact_id=artifact_vw.artifact_id' ;
 			}
 		}
@@ -342,6 +385,23 @@
 			$wheresql .= ')' ;
 		}
 
+		//add constraint on the summary string.
+		if ($this->summary) {
+			$wheresql .= ' AND summary LIKE $'.$paramcount++ ;
+			$params[] = $this->summary;
+		}
+		//add constraint on the description string.
+		if ($this->description) {
+			$wheresql .= ' AND details LIKE $'.$paramcount++ ;
+			$params[] = $this->description;
+		}
+		//add constraint on the followups string.
+		if ($this->followups) {
+			$wheresql .= 'LEFT OUTER JOIN artifact_message am USING (artifact_id)
+						WHERE am.body LIKE $'.$paramcount++;
+			$params[] = $this->followups;
+		}
+
 		$sortorder = util_ensure_value_in_set ($this->sort,
 						       array ('ASC', 'DESC')) ;
 		

Modified: trunk/gforge/common/tracker/ArtifactQuery.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactQuery.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/ArtifactQuery.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -34,6 +34,9 @@
 define('ARTIFACT_QUERY_SORTORD',6);
 define('ARTIFACT_QUERY_OPENDATE',7);
 define('ARTIFACT_QUERY_CLOSEDATE',8);
+define('ARTIFACT_QUERY_SUMMARY',9);
+define('ARTIFACT_QUERY_DESCRIPTION',10);
+define('ARTIFACT_QUERY_FOLLOWUPS',11);
 
 require_once $gfcommon.'tracker/ArtifactType.class.php';
 
@@ -89,12 +92,13 @@
 	}
 
 	/**
-	 *	create - create a row in the table that stores a saved query for 	 *	a tracker.   
+	 *	create - create a row in the table that stores a saved query for
+	 *  a tracker.   
 	 *
 	 *	@param	string	Name of the saved query.
-	 *  	@return 	true on success / false on failure.
+	 *  @return 	true on success / false on failure.
 	 */
-	function create($name,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange=0,$closedaterange=0) {
+	function create($name,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange=0,$closedaterange=0,$summary,$description,$followups,$query_type=0) {
 		//
 		//	data validation
 		//
@@ -112,11 +116,27 @@
 			return false;
 		}
 
+		if ($query_type>0 && !$this->ArtifactType->userIsAdmin()) {
+			$this->setError($Language->getText('artifact_query','require_admin_rights'));
+			return false;
+		}
+		
+		// Reset the project default query.
+		if ($query_type==2) {
+			$sql="UPDATE artifact_query SET query_type=1 WHERE query_type=2 AND group_artifact_id='".$this->ArtifactType->getID()."'";
+			$res=db_query($sql);
+			if (!$res) {
+				$this->setError('Error Updating: '.db_error());
+				return false;
+			}
+		}
+		
 		db_begin();
-		$result = db_query_params ('INSERT INTO artifact_query (group_artifact_id,query_name,user_id) VALUES ($1,$2,$3)',
+		$result = db_query_params ('INSERT INTO artifact_query (group_artifact_id,query_name,user_id,query_type) VALUES ($1,$2,$3,$4)',
 					   array ($this->ArtifactType->getID(),
 						  htmlspecialchars($name),
-						  user_getid())) ;
+						  user_getid(),
+						  $query_type)) ;
 		if ($result && db_affected_rows($result) > 0) {
 			$this->clearError();
 			$id=db_insertid($result,'artifact_query','artifact_query_id');
@@ -125,7 +145,7 @@
 				db_rollback();
 				return false;
 			} else {
-				if (!$this->insertElements($id,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange,$closedaterange)) {
+				if (!$this->insertElements($id,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange,$closedaterange,$summary,$description,$followups)) {
 					db_rollback();
 					return false;
 				}
@@ -191,7 +211,7 @@
 	 *
 	 *
 	 */
-	function insertElements($id,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange,$closedaterange) {
+	function insertElements($id,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange,$closedaterange,$summary,$description,$followups) {
 		$res = db_query_params ('DELETE FROM artifact_query_fields WHERE artifact_query_id=$1',
 					array ($id)) ;
 		if (!$res) {
@@ -310,7 +330,34 @@
 			$this->setError('Setting Sort Order: '.db_error());
 			return false;
 		}
+		
+		// Saving the summary value.
+		$res=db_query("INSERT INTO artifact_query_fields 
+			(artifact_query_id,query_field_type,query_field_id,query_field_values) 
+			VALUES ('$id','".ARTIFACT_QUERY_SUMMARY."','0','".$summary."')");
+		if (!$res) {
+			$this->setError('Setting Summary: '.db_error());
+			return false;
+		}
 
+		// Saving the description value.
+		$res=db_query("INSERT INTO artifact_query_fields 
+			(artifact_query_id,query_field_type,query_field_id,query_field_values) 
+			VALUES ('$id','".ARTIFACT_QUERY_DESCRIPTION."','0','".$description."')");
+		if (!$res) {
+			$this->setError('Setting Description: '.db_error());
+			return false;
+		}
+		
+		// Saving the followups value.
+		$res=db_query("INSERT INTO artifact_query_fields 
+			(artifact_query_id,query_field_type,query_field_id,query_field_values) 
+			VALUES ('$id','".ARTIFACT_QUERY_FOLLOWUPS."','0','".$followups."')");
+		if (!$res) {
+			$this->setError('Setting Followups: '.db_error());
+			return false;
+		}
+
 		if (!$extra_fields) {
 			$extra_fields=array();
 		}
@@ -329,8 +376,6 @@
 					$vals[$i][$e]=intval($vals[$i][$e]); 
 				}
 				$vals[$i]=implode(',',$vals[$i]);
-			} else {
-				$vals[$i] =	 intval($vals[$i]);
 			}
 			$res = db_query_params ('INSERT INTO artifact_query_fields 
 			(artifact_query_id,query_field_type,query_field_id,query_field_values) 
@@ -366,6 +411,24 @@
 	}
 
 	/**
+	 *	getUserId - get the user_id.
+	 *
+	 *	@return	string	The user_id.
+	 */
+	function getUserId() {
+		return $this->data_array['user_id'];
+	}
+
+	/**
+	 *	getQueryType - get the type of the query
+	 *
+	 *	@return	string	type of query (0: private, 1: project, 2: project&default)
+	 */
+	function getQueryType() {
+		return $this->data_array['query_type'];
+	}
+
+	/**
 	 *	getSortCol - the column that you're sorting on
 	 *
 	 *	@return	string	The column name.
@@ -423,6 +486,45 @@
 	}
 
 	/**
+	 *	getSummary - get the summary string to include in a query
+	 *
+	 *	@return	string	Summary string.
+	 */
+	function getSummary() {
+		if ($this->element_array[ARTIFACT_QUERY_SUMMARY][0]) {
+			return $this->element_array[ARTIFACT_QUERY_SUMMARY][0];
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 *	getDescription - get the description string to include in a query
+	 *
+	 *	@return	string	Description string.
+	 */
+	function getDescription() {
+		if ($this->element_array[ARTIFACT_QUERY_DESCRIPTION][0]) {
+			return $this->element_array[ARTIFACT_QUERY_DESCRIPTION][0];
+		} else {
+			return false;
+		}
+	}
+
+	/**
+	 *	getFollowups - get the followups string to include in a query
+	 *
+	 *	@return	string	Folowups string.
+	 */
+	function getFollowups() {
+		if ($this->element_array[ARTIFACT_QUERY_FOLLOWUPS][0]) {
+			return $this->element_array[ARTIFACT_QUERY_FOLLOWUPS][0];
+		} else {
+			return false;
+		}
+	}
+
+	/**
 	 *	getAssignee
 	 *
 	 *	@return	string	Assignee ID
@@ -466,7 +568,7 @@
 	 *	@param	string	The name of the saved query
 	 *  @return	boolean	success.
 	 */
-	function update($name,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange='',$closedaterange='') {
+	function update($name,$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange='',$closedaterange='',$summary,$description,$followups,$query_type=0) {
 		if (!$name) {
 			$this->setMissingParamsError();
 			return false;
@@ -479,16 +581,32 @@
 			$this->setError(_('Query does not exist'));
 			return false;
 		}
+		if ($query_type>0 && !$this->ArtifactType->userIsAdmin()) {
+			$this->setError($Language->getText('artifact_query','require_admin_rights'));
+			return false;
+		}
+		
+		// Reset the project default query.
+		if ($query_type==2) {
+			$sql="UPDATE artifact_query SET query_type=1 WHERE query_type=2 AND group_artifact_id='".$this->ArtifactType->getID()."'";
+			$res=db_query($sql);
+			if (!$res) {
+				$this->setError('Error Updating: '.db_error());
+				return false;
+			}
+		}
 		db_begin();
 		$result = db_query_params ('UPDATE artifact_query
-			SET query_name=$1
-			WHERE artifact_query_id=$2
-			AND user_id=$3',
+			SET query_name=$1,
+				query_type=$2
+			WHERE artifact_query_id=$3
+			AND user_id=$4',
 					   array (htmlspecialchars($name),
+						  $query_type,
 						  $this->getID(),
 						  user_getid())) ;
 		if ($result && db_affected_rows($result) > 0) {
-			if (!$this->insertElements($this->getID(),$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange,$closedaterange)) {
+			if (!$this->insertElements($this->getID(),$status,$assignee,$moddaterange,$sort_col,$sort_ord,$extra_fields,$opendaterange,$closedaterange,$summary,$description,$followups)) {
 				db_rollback();
 				return false;
 			} else {
@@ -518,9 +636,15 @@
 	}
 
 	function delete() {
-		$res = db_query_params ('DELETE FROM artifact_query WHERE artifact_query_id=$1 AND user_id=$2',
+		if ($this->ArtifactType->userIsAdmin()) {
+			$res = db_query_params ('DELETE FROM artifact_query WHERE artifact_query_id=$1 AND (user_id=$2 OR query_type>0)',
 					array ($this->getID(),
 					       user_getid())) ;
+		} else {
+			$res = db_query_params ('DELETE FROM artifact_query WHERE artifact_query_id=$1 AND user_id=$2',
+					array ($this->getID(),
+					       user_getid())) ;
+		}
 		$res = db_query_params ('DELETE FROM user_preferences WHERE preference_value=$1 AND preference_name =$2',
 					array ($this->getID(),
 					       'art_query'.$this->ArtifactType->getID())) ;
@@ -536,7 +660,7 @@
 	function Exist($name) {
 		$user_id = user_getid();
 		$art_id = $this->ArtifactType->getID();
-		$res = db_query_params ('SELECT * FROM artifact_query WHERE group_artifact_id = $1 AND query_name = $2 AND user_id = $3',
+		$res = db_query_params ('SELECT * FROM artifact_query WHERE group_artifact_id = $1 AND query_name = $2 AND (user_id = $3 OR query_type>0)',
 					array ($art_id,
 					       $name,
 					       $user_id)) ;

Modified: trunk/gforge/common/tracker/ArtifactType.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactType.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/ArtifactType.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -25,6 +25,7 @@
  */
 
 require_once $gfcommon.'include/Error.class.php';
+require_once $gfcommon.'tracker/ArtifactExtraFieldElement.class.php';
 
 	/**
 	* Gets an ArtifactType object from the artifact type id
@@ -270,9 +271,9 @@
 			} else {
 				db_commit();
 				return $id;
-			}
 		}
 	}
+	}
 
 	/**
 	 *  fetchData - re-fetch the data for this ArtifactType from the database.
@@ -723,7 +724,7 @@
 			$res = db_query_params  ('SELECT element_id,element_name,status_id
 				FROM artifact_extra_field_elements
 				WHERE extra_field_id = $1
-				ORDER BY element_id ASC',
+				ORDER BY element_pos ASC, element_id ASC',
 						 array ($id)) ;
 			$i=0;
 			while($arr =& db_fetch_array($res)) {
@@ -976,7 +977,8 @@
 				//
 				//	You must have a role in the project if this tracker is not public
 				//
-				if ($this->userIsAdmin() || $this->getCurrentUserPerm() >= 0) {
+				$perm = $this->getCurrentUserPerm();
+				if ($this->userIsAdmin() || (strlen($perm) && $perm >= 0)) {
 					return true;
 				} else {
 					return false;
@@ -1122,6 +1124,40 @@
 		}
 	}
 
+	/**
+	 *	  getBrowseList - get the free-form string strings.
+	 *
+	 *	  @return string instructions.
+	 */
+	function getBrowseList() {
+		$list = $this->data_array['browse_list'];
+		
+		// remove status_id in the browse list if a custom status exists
+    if (count($this->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_STATUS)) > 0) {
+      $arr = explode(',', $list);
+      $idx = array_search('status_id', $arr);
+      if($idx !== False) {
+        array_splice($arr, $idx, 1);
+      }
+      return join(',', $arr);
+    }
+
+    return $list;
+	}
+
+	/**
+	 *	setCustomStatusField - set the extra_field_id of the field containing the custom status.
+	 *	@param	int	The extra field id.
+	 *	@return	boolean	success.
+	 */
+	function setBrowseList($list) {
+		$res=db_query("UPDATE artifact_group_list 
+		    SET browse_list='$list'
+			WHERE group_artifact_id='".$this->getID()."'");
+		$this->fetchData($this->getID());
+		return $res;
+	}
+
 }
 
 // Local Variables:

Added: trunk/gforge/common/tracker/ArtifactWorkflow.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactWorkflow.class.php	                        (rev 0)
+++ trunk/gforge/common/tracker/ArtifactWorkflow.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,282 @@
+<?php
+/**
+ * The ArtifactWorkflow class manages workflow for trackers.
+ * 
+ * The workflow is attached to custom status field only.
+ * 
+ * Associated tables are:
+ * - artifact_workflow_event  : to track allowed events.
+ * - artifact_workflow_roles  : to track roles allowed to perform an event.
+ * - artifact_workflow_notify : to track notification associated to an event (not implemented).
+ * 
+ * An event is a transition from one value to another.
+ * 
+ * NOTE: Code should be improved to manage any kind of custom fields not only of type
+ * 'Status' but maybe also for the 'select' also.
+ * 
+ * 2008 : Alain Peyrat <alain.peyrat at alcatel-lucent.fr>
+ * 
+ * NOTES:
+ * @todo: the getAllowedRoles should be replaced by getRealAllowedRoles code. (to be tested).
+ * @todo: Some code could use a db direct to array func instead of the while.
+ * 
+ */
+require_once 'common/include/Error.class.php';
+
+class ArtifactWorkflow extends Error {
+	
+	var $ath;
+	var $artifact_id;
+	var $field_id;
+	
+	function ArtifactWorkflow($artifact, $field_id) {
+		$this->ath = $artifact;
+		$this->artifact_id = (int)$artifact->getID();
+		$this->field_id = (int)$field_id;
+		return true;
+	}
+	
+	// Check if the following event is allowed or not.
+	// return true is allowed, false if not.
+	function checkEvent($from, $to) {
+		if ($from === $to)
+			return true;
+
+		$sql = "SELECT event_id FROM artifact_workflow_event 
+				WHERE group_artifact_id=".$this->artifact_id."
+				AND field_id=".$this->field_id."
+				AND from_value_id=".$from."
+				AND to_value_id=".$to;
+		$res = db_query($sql);
+		$event_id = db_result($res, 0, 'event_id');
+		if ($event_id) {
+			// No role based checks for the initial transition.
+			if ($from == 100) 
+				return true;
+
+			// There is a transition, now check if current role is allowed.
+			$sql = "SELECT event_id 
+					FROM user_group, artifact_workflow_roles 
+					WHERE user_id=".user_getid()."
+					AND group_id=".$this->ath->Group->getID()."
+					AND event_id=$event_id 
+					AND user_group.role_id=artifact_workflow_roles.role_id";
+			return db_result(db_query($sql), 0, 'event_id') ? true : false;
+		}
+		return false;
+	}
+	
+	function getNotifyFromWorkFlow() {
+		
+	}
+	
+	/*
+	 * When a new element is created, add all the new events in the workflow.
+	 */
+	function addNode($element_id) {
+		$elearray = $this->ath->getExtraFieldElements($this->field_id);
+		foreach ($elearray as $e) {
+			if ($element_id !== $e['element_id']) {
+				$this->_addEvent($e['element_id'], $element_id);
+				$this->_addEvent($element_id, $e['element_id']);
+			}
+		}
+		
+		// Allow the new element for the Submit form (Initial values).
+		$this->_addEvent('100', $element_id);
+	}
+	
+	// Returns all the possible following nodes (no roles involved).
+	function getNextNodes($from) {
+		$sql = "SELECT to_value_id FROM artifact_workflow_event 
+				WHERE group_artifact_id=".$this->artifact_id."
+				AND field_id=".$this->field_id."
+				AND from_value_id=".(int)$from;
+		$res = db_query($sql);
+		$values = array();
+		while($arr = db_fetch_array($res)) {
+			$values[] = $arr['to_value_id'];
+		}
+		return $values;
+		
+	}
+
+	
+	function saveNextNodes($from, $nodes) {
+
+		// Get All possible nodes.
+		$current = $this->getNextNodes($from);
+
+		// Remove events no longer present.
+		foreach ($current as $node) {
+			if (!in_array($node, $nodes)) {
+				if ($from != $node) {
+					$this->_removeEvent($from, $node);
+				}
+			}
+		}
+
+		// Add missing events.
+		foreach ($nodes as $node) {
+			if (!in_array($node, $current)) {
+				$this->_addEvent($from, $node);
+			}
+		}
+	}
+	
+	function getAllowedRoles($from, $to) {
+		$values = $this->_getRealAllowedRoles($from, $to);
+				
+		// If no values, then no roles defined, all roles are allowed.
+		if (empty($values)) {
+			$res=db_query("SELECT role_id 
+			FROM role WHERE group_id='".$this->ath->Group->getID()."' ORDER BY role_name");
+			while($arr = db_fetch_array($res)) {
+				$values[] = $arr['role_id'];
+			}			
+		}
+		return $values;
+	}
+
+	
+	function saveAllowedRoles($from, $to, $roles) {
+
+		$event_id = $this->_getEventId($from, $to);
+
+		// Get All possible roles.
+		$current = $this->_getRealAllowedRoles($from, $to);
+
+		// Remove roles no longer present.
+		foreach ($current as $role) {
+			if (!in_array($role, $roles)) {
+				$this->_removeRole($event_id, $role);
+			}
+		}
+
+		// Add missing roles.
+		foreach ($roles as $role) {
+			if (!in_array($role, $current)) {
+				$this->_addRole($event_id, $role);
+			}
+		}
+	}
+	
+	function _getEventId($from, $to) {
+		$sql = "SELECT event_id FROM artifact_workflow_event 
+				WHERE group_artifact_id=".$this->artifact_id."
+				AND field_id=".$this->field_id."
+				AND from_value_id=".$from."
+				AND to_value_id=".$to;
+		$res = db_query($sql);
+		if (!$res) {
+			$this->setError('Unable to get Event Id ($from, $to): '.db_error());
+			return false;
+		}
+		return db_result($res, 0, 'event_id');
+		
+	}
+
+	
+	function _addEvent($from, $to) {
+		$sql = "INSERT INTO artifact_workflow_event
+				(group_artifact_id, field_id, from_value_id, to_value_id)
+				VALUES (".$this->artifact_id.", ".$this->field_id.", $from, $to)";
+		$res = db_query($sql);
+		if (!$res) {
+			$this->setError('Unable to add Event($from, $to): '.db_error());
+			return false;
+		}
+		
+		$event_id = $this->_getEventId($from, $to);
+		if ($event_id) {
+			// By default, all roles are allowed on a new event.
+			$res=db_query("SELECT role_id 
+				FROM role WHERE group_id='".$this->ath->Group->getID()."' ORDER BY role_name");
+			while($arr = db_fetch_array($res)) {
+				$this->_addRole($event_id, $arr['role_id']);
+			}
+		}
+
+		return true;
+	}
+
+	
+	function _removeEvent($from, $to) {
+		$event_id = $this->_getEventId($from, $to);
+		
+		$sql = "DELETE FROM artifact_workflow_event
+				WHERE group_artifact_id=".$this->artifact_id."
+				AND field_id=".$this->field_id."
+				AND from_value_id=".$from."
+				AND to_value_id=".$to;
+		$res = db_query($sql);
+		if (!$res) {
+			$this->setError('Unable to remove Event($from, $to): '.db_error());
+			return false;
+		}
+		
+		return true;
+	}
+
+	function _getRealAllowedRoles($from, $to) {
+		$sql = "SELECT role_id
+				FROM artifact_workflow_roles, artifact_workflow_event
+				WHERE artifact_workflow_roles.event_id = artifact_workflow_event.event_id
+				AND group_artifact_id=".$this->artifact_id."
+				AND field_id=".$this->field_id."
+				AND from_value_id=".$from."
+				AND to_value_id=".$to;
+		$res = db_query($sql);
+		$values = array();
+		while($arr = db_fetch_array($res)) {
+			$values[] = $arr['role_id'];
+		}
+		return $values;
+	}
+
+	function _addRole($event_id, $role_id) {
+		$sql = "INSERT INTO artifact_workflow_roles
+				(event_id, role_id)
+				VALUES ($event_id, $role_id)";
+		$res = db_query($sql);
+		if (!$res) {
+			$this->setError('Unable to add Role ($role_id): '.db_error());
+			return false;
+		}
+		return true;
+		
+	}
+	
+	function _removeRole($event_id, $role_id) {
+		$sql = "DELETE FROM artifact_workflow_roles
+				WHERE event_id=$event_id AND role_id=$role_id";
+		$res = db_query($sql);
+		if (!$res) {
+			$this->setError('Unable to remove Event($from, $to): '.db_error());
+			return false;
+		}
+		return true;
+		
+	}
+	
+}
+
+/*
+ * Update the required information in the workflow when a new role is created.
+ * In this case, for all the defined events, add the role as allowed.
+ */
+function workflow_add_new_role ($role_id, $group) {
+	$sql = "INSERT INTO artifact_workflow_roles 
+			SELECT event_id, $role_id as role_id 
+					FROM artifact_workflow_event, artifact_group_list
+					WHERE artifact_workflow_event.group_artifact_id=artifact_group_list.group_artifact_id 
+					AND artifact_group_list.group_id=".$group->getID();
+	$res = db_query($sql);
+	if (!$res) {
+		$this->setError('Unable to register new role in workflows: '.db_error());
+		return false;
+	}
+	return true;
+}
+
+?>

Modified: trunk/gforge/common/tracker/ArtifactsForUser.class.php
===================================================================
--- trunk/gforge/common/tracker/ArtifactsForUser.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/common/tracker/ArtifactsForUser.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -90,6 +90,43 @@
 		return $this->getArtifactsFromSQLwithParams('SELECT * FROM artifact_vw av WHERE av.submitted_by=$1 AND av.status_id=1 ORDER BY av.group_artifact_id, av.artifact_id DESC',
 							    array($this->User->getID())) ;
 	}
+
+	/**
+	*	getMonitoredArtifacts
+	*
+	*	@return Artifact[] The array of Artifacts
+	*/
+	function & getMonitoredArtifacts() {
+		$artifacts = array();
+		$sql="SELECT groups.group_name,groups.group_id," .
+				"artifact_group_list.group_artifact_id," .
+				"artifact_group_list.name " .
+			"FROM groups,artifact_group_list,artifact_type_monitor " .
+			"WHERE groups.group_id=artifact_group_list.group_id " .
+			"AND groups.status ='A' " .
+			"AND artifact_group_list.group_artifact_id=artifact_type_monitor.group_artifact_id " .
+			"AND artifact_type_monitor.user_id='".$this->User->getID()."' " .
+			"ORDER BY group_name DESC";
+			
+		$result=db_query($sql);
+		$rows=db_numrows($result);
+		if ($rows < 1) {
+		        return $artifacts;
+		}
+		for ($i=0; $i<$rows; $i++) {
+			$group_id = db_result($result,$i,'group_id');
+			$group_artifact_id = db_result($result,$i,'group_artifact_id');
+			$group =& group_get_object($group_id);
+			$artifact =& new ArtifactType($group,$group_artifact_id);
+			$ag = $artifact->getGroup();
+			if ($artifact->isError()) {
+				$this->setError($artifact->getErrorMessage());
+			} else {
+				$artifacts[] =& $artifact;
+			}
+		}
+		return $artifacts;
+	}
 }
 
 // Local Variables:

Modified: trunk/gforge/db/20070924-artifact-perm.sql
===================================================================
--- trunk/gforge/db/20070924-artifact-perm.sql	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/db/20070924-artifact-perm.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -1,6 +1,7 @@
 DROP VIEW artifactperm_artgrouplist_vw ;
 DROP VIEW artifactperm_user_vw ;
 DROP TABLE artifact_perm ;
+DROP VIEW artifact_perm ;
 
 CREATE VIEW artifact_perm AS
        SELECT

Modified: trunk/gforge/db/20070924-forum-perm.sql
===================================================================
--- trunk/gforge/db/20070924-forum-perm.sql	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/db/20070924-forum-perm.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -1,4 +1,5 @@
 DROP TABLE forum_perm ;
+DROP VIEW forum_perm ;
 
 CREATE VIEW forum_perm AS
        SELECT

Modified: trunk/gforge/db/20070924-project-perm.sql
===================================================================
--- trunk/gforge/db/20070924-project-perm.sql	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/db/20070924-project-perm.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -1,4 +1,5 @@
 DROP TABLE project_perm ;
+DROP VIEW project_perm ;
 
 CREATE VIEW project_perm AS
        SELECT

Modified: trunk/gforge/db/20090327_create_table_project_tags.sql
===================================================================
--- trunk/gforge/db/20090327_create_table_project_tags.sql	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/db/20090327_create_table_project_tags.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -1,6 +1,7 @@
 --
 -- Create table for project's tags
 --
+DROP TABLE project_tags ;
 
 CREATE TABLE project_tags
 (

Added: trunk/gforge/db/20090507-add_artifact_workflow.sql
===================================================================
--- trunk/gforge/db/20090507-add_artifact_workflow.sql	                        (rev 0)
+++ trunk/gforge/db/20090507-add_artifact_workflow.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,73 @@
+-- DEBUG code, drop before
+-- DROP SEQUENCE artifact_workflow_event_id_seq;
+-- DROP INDEX artifact_workflow_event_index;
+-- DROP TABLE artifact_workflow_event CASCADE;
+-- DROP TABLE artifact_workflow_roles CASCADE;
+-- DROP TABLE artifact_workflow_notify CASCADE;
+-- ALTER TABLE artifact_extra_field_list DROP CONSTRAINT artifact_extra_field_list_unique;
+
+ALTER TABLE artifact_extra_field_list ADD CONSTRAINT artifact_extra_field_list_unique UNIQUE (group_artifact_id, extra_field_id);
+
+-- Table: artifact_workflow_event
+
+CREATE SEQUENCE artifact_workflow_event_id_seq
+  INCREMENT 1
+  MINVALUE 1
+  MAXVALUE 2147483647
+  START 1
+  CACHE 1;
+-- ALTER TABLE artifact_workflow_event_id_seq OWNER TO gforge;
+
+CREATE TABLE artifact_workflow_event
+(
+  event_id integer NOT NULL DEFAULT nextval('"artifact_workflow_event_id_seq"'::text),
+  group_artifact_id integer NOT NULL,
+  field_id integer NOT NULL,
+  from_value_id integer NOT NULL,
+  to_value_id integer NOT NULL,
+  CONSTRAINT artifact_workflow_event_pkey PRIMARY KEY (event_id),
+  CONSTRAINT artifact_workflow_event_group_artifact_id_fkey FOREIGN KEY (group_artifact_id, field_id) 
+	REFERENCES artifact_extra_field_list (group_artifact_id, extra_field_id) MATCH SIMPLE
+	ON UPDATE NO ACTION ON DELETE CASCADE
+) 
+WITH OIDS;
+-- ALTER TABLE artifact_workflow_event OWNER TO gforge;
+
+-- Index: artifact_workflow_event_index
+
+CREATE INDEX artifact_workflow_event_index
+  ON artifact_workflow_event
+  USING btree
+  (event_id, group_artifact_id, field_id);
+
+
+
+-- Table: artifact_workflow_roles
+
+CREATE TABLE artifact_workflow_roles
+(
+  event_id integer NOT NULL,
+  role_id integer NOT NULL,
+  CONSTRAINT artifact_workflow_roles_pkey PRIMARY KEY (event_id, role_id),
+  CONSTRAINT artifact_workflow_roles_event_id_fkey FOREIGN KEY (event_id)
+      REFERENCES artifact_workflow_event (event_id) MATCH SIMPLE
+      ON UPDATE NO ACTION ON DELETE CASCADE
+)
+WITH OIDS;
+-- ALTER TABLE artifact_workflow_roles OWNER TO gforge;
+
+
+-- Table: artifact_workflow_notify
+
+CREATE TABLE artifact_workflow_notify
+(
+  event_id integer NOT NULL,
+  role_id integer NOT NULL,
+  CONSTRAINT artifact_workflow_notify_pkey PRIMARY KEY (event_id, role_id),
+  CONSTRAINT artifact_workflow_notify_event_id_fkey FOREIGN KEY (event_id)
+      REFERENCES artifact_workflow_event (event_id) MATCH SIMPLE
+      ON UPDATE NO ACTION ON DELETE CASCADE
+) 
+WITH OIDS;
+-- ALTER TABLE artifact_workflow_notify OWNER TO gforge;
+

Added: trunk/gforge/db/20090507-add_element_pos.sql
===================================================================
--- trunk/gforge/db/20090507-add_element_pos.sql	                        (rev 0)
+++ trunk/gforge/db/20090507-add_element_pos.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,7 @@
+--
+-- Add element_pos column in artifact_extra_field_elements
+-- for AC_FD0424
+-- 
+-- Roger Guignard May-2008
+--
+ALTER TABLE "artifact_extra_field_elements" ADD COLUMN "element_pos" integer;

Added: trunk/gforge/db/20090507-add_project_query.sql
===================================================================
--- trunk/gforge/db/20090507-add_project_query.sql	                        (rev 0)
+++ trunk/gforge/db/20090507-add_project_query.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,4 @@
+ALTER TABLE "artifact_query" ADD COLUMN "query_type" integer;
+UPDATE "artifact_query" SET query_type=0;
+ALTER TABLE "artifact_query" ALTER COLUMN "query_type" SET DEFAULT 0;
+ALTER TABLE "artifact_query" ALTER COLUMN "query_type" SET NOT NULL;

Added: trunk/gforge/db/20090507-browse_list.sql
===================================================================
--- trunk/gforge/db/20090507-browse_list.sql	                        (rev 0)
+++ trunk/gforge/db/20090507-browse_list.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,25 @@
+--
+-- Add the browse_list field in the artifact_group_list
+--
+-- This is to support the customization of columns in the 
+-- browse list.
+-- 
+-- Alain Peyrat Sep-2006
+
+DROP VIEW "artifact_group_list_vw";
+
+ALTER TABLE "artifact_group_list" ADD COLUMN "browse_list" text;
+UPDATE "artifact_group_list" 
+  SET browse_list='summary,open_date,assigned_to,submitted_by';
+ALTER TABLE "artifact_group_list" ALTER COLUMN browse_list SET NOT NULL;
+ALTER TABLE "artifact_group_list" ALTER COLUMN browse_list
+  SET DEFAULT 'summary,open_date,assigned_to,submitted_by';
+
+CREATE VIEW "artifact_group_list_vw" AS 
+  SELECT agl.group_artifact_id, agl.group_id, agl.name, agl.description,
+    agl.is_public, agl.allow_anon, agl.email_all_updates, agl.email_address,
+    agl.due_period, agl.submit_instructions, agl.browse_instructions, 
+    agl.browse_list, agl.datatype, agl.status_timeout, agl.custom_status_field, 
+    agl.custom_renderer, aca.count, aca.open_count
+  FROM artifact_group_list agl
+  LEFT JOIN artifact_counts_agg aca USING (group_artifact_id);


Property changes on: trunk/gforge/db/20090507-browse_list.sql
___________________________________________________________________
Added: svn:executable
   + *

Added: trunk/gforge/db/20090507-install_workflow.php
===================================================================
--- trunk/gforge/db/20090507-install_workflow.php	                        (rev 0)
+++ trunk/gforge/db/20090507-install_workflow.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,44 @@
+<?php
+
+require_once dirname(__FILE__).'/../www/env.inc.php';
+require_once $gfwww.'include/squal_pre.php';
+require_once $gfcommon.'tracker/Artifact.class.php';
+require_once $gfcommon.'tracker/ArtifactFile.class.php';
+require_once $gfwww.'tracker/include/ArtifactFileHtml.class.php';
+require_once $gfcommon.'common/tracker/ArtifactType.class.php';
+require_once $gfcommon.'tracker/ArtifactTypeFactory.class.php';
+require_once $gfcommon.'tracker/include/ArtifactTypeHtml.class.php';
+require_once $gfwww.'tracker/include/ArtifactHtml.class.php';
+require_once $gfcommon.'tracker/ArtifactCanned.class.php';
+require_once $gfcommon.'tracker/ArtifactExtraField.class.php';
+require_once $gfcommon.'tracker/ArtifactExtraFieldElement.class.php';
+require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
+
+/* Need full power, switching to an admin guy */
+$res = db_query("SELECT user_id FROM user_group WHERE group_id=1");
+$admin_id = db_result($res,0,'user_id');
+session_set_new($admin_id);
+
+$sql = "SELECT group_id, artifact_group_list.group_artifact_id, element_id, artifact_extra_field_elements.extra_field_id
+		FROM artifact_extra_field_list, artifact_extra_field_elements, artifact_group_list
+		WHERE 
+			artifact_extra_field_list.extra_field_id=artifact_extra_field_elements.extra_field_id
+		AND 	artifact_group_list.group_artifact_id = artifact_extra_field_list.group_artifact_id
+		AND	field_type=7";
+
+$res = db_query($sql);
+while($row = db_fetch_array($res)) {
+	print "Upgrading group_id=".$row['group_id']." (group_artifact_id=".$row['group_artifact_id'].")\n";
+	$group =& group_get_object($row['group_id']);
+	$ath = new ArtifactTypeHtml($group, $row['group_artifact_id']);
+	$efarr =& $ath->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_STATUS);
+	$keys=array_keys($efarr);
+	$field_id = $keys[0];
+	    	
+	$atw = new ArtifactWorkflow($ath, $field_id);
+	$atw->addNode($row['element_id']);
+	$atw->_addEvent('100', $row['element_id']);
+}
+
+echo "SUCCESS";
+?>

Modified: trunk/gforge/db/gforge.sql
===================================================================
--- trunk/gforge/db/gforge.sql	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/db/gforge.sql	2009-05-18 21:33:45 UTC (rev 7687)
@@ -2242,7 +2242,8 @@
     element_id integer DEFAULT nextval('artifact_extra_field_elements_element_id_seq'::text) NOT NULL,
     extra_field_id integer NOT NULL,
     element_name text NOT NULL,
-    status_id integer DEFAULT 0 NOT NULL
+    status_id integer DEFAULT 0 NOT NULL,
+    element_pos integer
 );
 
 
@@ -2609,7 +2610,8 @@
     artifact_query_id serial NOT NULL,
     group_artifact_id integer NOT NULL,
     user_id integer NOT NULL,
-    query_name text NOT NULL
+    query_name text NOT NULL,
+    query_type integer DEFAULT 0 NOT NULL
 );
 
 
@@ -7808,5 +7810,85 @@
 	FROM rep_group_act_monthly
 	GROUP BY month;
 
+DROP VIEW "artifact_group_list_vw";
 
+ALTER TABLE "artifact_group_list" ADD COLUMN "browse_list" text;
+UPDATE "artifact_group_list" 
+  SET browse_list='summary,open_date,assigned_to,submitted_by';
+ALTER TABLE "artifact_group_list" ALTER COLUMN browse_list SET NOT NULL;
+ALTER TABLE "artifact_group_list" ALTER COLUMN browse_list
+  SET DEFAULT 'summary,open_date,assigned_to,submitted_by';
 
+CREATE VIEW "artifact_group_list_vw" AS 
+  SELECT agl.group_artifact_id, agl.group_id, agl.name, agl.description,
+    agl.is_public, agl.allow_anon, agl.email_all_updates, agl.email_address,
+    agl.due_period, agl.submit_instructions, agl.browse_instructions, 
+    agl.browse_list, agl.datatype, agl.status_timeout, agl.custom_status_field, 
+    agl.custom_renderer, aca.count, aca.open_count
+  FROM artifact_group_list agl
+  LEFT JOIN artifact_counts_agg aca USING (group_artifact_id);
+ALTER TABLE artifact_extra_field_list ADD CONSTRAINT artifact_extra_field_list_unique UNIQUE (group_artifact_id, extra_field_id);
+
+-- Table: artifact_workflow_event
+
+CREATE SEQUENCE artifact_workflow_event_id_seq
+  INCREMENT 1
+  MINVALUE 1
+  MAXVALUE 2147483647
+  START 1
+  CACHE 1;
+-- ALTER TABLE artifact_workflow_event_id_seq OWNER TO gforge;
+
+CREATE TABLE artifact_workflow_event
+(
+  event_id integer NOT NULL DEFAULT nextval('"artifact_workflow_event_id_seq"'::text),
+  group_artifact_id integer NOT NULL,
+  field_id integer NOT NULL,
+  from_value_id integer NOT NULL,
+  to_value_id integer NOT NULL,
+  CONSTRAINT artifact_workflow_event_pkey PRIMARY KEY (event_id),
+  CONSTRAINT artifact_workflow_event_group_artifact_id_fkey FOREIGN KEY (group_artifact_id, field_id) 
+	REFERENCES artifact_extra_field_list (group_artifact_id, extra_field_id) MATCH SIMPLE
+	ON UPDATE NO ACTION ON DELETE CASCADE
+) 
+WITH OIDS;
+-- ALTER TABLE artifact_workflow_event OWNER TO gforge;
+
+-- Index: artifact_workflow_event_index
+
+CREATE INDEX artifact_workflow_event_index
+  ON artifact_workflow_event
+  USING btree
+  (event_id, group_artifact_id, field_id);
+
+
+
+-- Table: artifact_workflow_roles
+
+CREATE TABLE artifact_workflow_roles
+(
+  event_id integer NOT NULL,
+  role_id integer NOT NULL,
+  CONSTRAINT artifact_workflow_roles_pkey PRIMARY KEY (event_id, role_id),
+  CONSTRAINT artifact_workflow_roles_event_id_fkey FOREIGN KEY (event_id)
+      REFERENCES artifact_workflow_event (event_id) MATCH SIMPLE
+      ON UPDATE NO ACTION ON DELETE CASCADE
+)
+WITH OIDS;
+-- ALTER TABLE artifact_workflow_roles OWNER TO gforge;
+
+
+-- Table: artifact_workflow_notify
+
+CREATE TABLE artifact_workflow_notify
+(
+  event_id integer NOT NULL,
+  role_id integer NOT NULL,
+  CONSTRAINT artifact_workflow_notify_pkey PRIMARY KEY (event_id, role_id),
+  CONSTRAINT artifact_workflow_notify_event_id_fkey FOREIGN KEY (event_id)
+      REFERENCES artifact_workflow_event (event_id) MATCH SIMPLE
+      ON UPDATE NO ACTION ON DELETE CASCADE
+) 
+WITH OIDS;
+-- ALTER TABLE artifact_workflow_notify OWNER TO gforge;
+

Modified: trunk/gforge/db/upgrade-db.php
===================================================================
--- trunk/gforge/db/upgrade-db.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/db/upgrade-db.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -266,7 +266,7 @@
 				show("Continue executing ([Y]es/[N]o)?\n");
 				// Read the input
 				$answer = strtolower(trim(fgets(STDIN)));
-				if ($answer != 'y' && $anser != 'yes') {
+				if ($answer != 'y' && $answer != 'yes') {
 					//db_rollback();
 					return false;
 				} else {
@@ -382,6 +382,10 @@
 }
 
 function drop_if_exists($name, $command, $kind) {
+	// Strip "name" => name
+	if (preg_match('/^"(.*)"$/', $name, $match)) {
+		$name = $match[1];
+	}
 	$res = db_query("SELECT COUNT(*) AS exists FROM pg_class WHERE relname='$name' AND relkind='$kind'");
 	if (!$res) {
 		show("ERROR:".db_error()."\n");

Modified: trunk/gforge/www/account/index.php
===================================================================
--- trunk/gforge/www/account/index.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/account/index.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -105,9 +105,9 @@
 <?php echo _('Account options:'); ?>
 </p>
 <ul>
-	<li><?php echo util_make_link_u ($u->getUnixName(),$u->getId(),'<strong>'._('View My Profile').'</strong>'); ?></a></li>
+	<li><?php echo util_make_link_u ($u->getUnixName(),$u->getId(),'<strong>'._('View My Profile').'</strong>'); ?></li>
 <?php if($GLOBALS['sys_use_people']) { ?>
-	<li><?php echo util_make_link ('/people/editprofile.php','<strong>'._('Edit My Skills Profile').'</strong>'); ?></a></li>
+	<li><?php echo util_make_link ('/people/editprofile.php','<strong>'._('Edit My Skills Profile').'</strong>'); ?></li>
 <?php } ?>
 </ul>
 <?php echo $HTML->boxBottom(); ?>

Modified: trunk/gforge/www/include/Layout.class.php
===================================================================
--- trunk/gforge/www/include/Layout.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/include/Layout.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -640,7 +640,7 @@
 
 	}
 
-	function tabGenerator($TABS_DIRS,$TABS_TITLES,$nested=false,$selected=false,$sel_tab_bgcolor='WHITE',$total_width='100%') {
+	function tabGenerator($TABS_DIRS,$TABS_TITLES,$nested=false,$selected=false,$sel_tab_bgcolor='white',$total_width='100%') {
 
 		$count=count($TABS_DIRS);
 		$width=intval((100/$count));
@@ -788,7 +788,7 @@
 
 //		print '<br />';
 //		print '
-//		<input type="CHECKBOX" name="exact" value="1"'.( $exact ? ' CHECKED' : ' UNCHECKED' ).'> Require All Words';
+//		<input type="checkbox" name="exact" value="1"'.( $exact ? ' checked' : ' unchecked' ).'> Require All Words';
 
 		print '</td><td>&nbsp;';
 		$parameters = $searchManager->getParameters();
@@ -869,9 +869,13 @@
 				$countLines += 3;
 			}
 		}
-		$breakLimit = round($countLines/3);
+
+ 		$maxCol = 3;
+ 		$breakLimit = ceil($countLines/$maxCol);
 		$break = $breakLimit;
 		$countLines = 0;
+ 		$countCol = 1;
+ 
 		$return = '
 			<table width="100%" border="0" cellspacing="0" cellpadding="1">
 				<tr class="tableheader">
@@ -881,7 +885,7 @@
 								<!--<td colspan="2">'._('Search in').':</td-->
 								<td align="right">'._('Select').' <a href="javascript:setCheckBoxes(\'\', true)">'._('all').'</a> / <a href="javascript:setCheckBoxes(\'\', false)">'._('none').'</a></td>
 							</tr>
-							<tr height="20" class="tablecontent">
+							<tr class="tablecontent">
 								<td colspan="3">&nbsp;</td>
 							</tr>
 							<tr align="center" valign="top" class="tablecontent">
@@ -895,10 +899,11 @@
 			}
 				
 			if ($countLines >= $break) {
-				//if the next block is so large that shifting it to the next column hits the breakpoint better
-				//the second part of statement (behind &&) proofs, that no 4th column is added
-				if ((($countLines - $break) >= ($break - $countLines)) && ((($break + $breakLimit)/$breakLimit) <= 3)) {
+ 				// if we are closer to the limit with this one included, then
+ 				// it's better to include it.
+ 				if (($countCol < $maxCol) && ($countLines - $break) >= ($break - $oldcountlines)) {
 					$return .= '</td><td>';
+ 					$countCol++;
 					$break += $breakLimit;
 				}
 			}
@@ -926,8 +931,8 @@
 				foreach($section as $underkey => $undersection) {
 					$return .= '	<input type="checkbox" name="'.urlencode($key.$underkey).'"';
 					if (isset($GLOBALS[urlencode($key.$underkey)]))
-						$return .= ' checked ';
-					$return .= '></input>'.$undersection.'<br />';				
+						$return .= ' checked="checked" ';
+					$return .= ' />'.$undersection.'<br />';				
 					
 				}
 				
@@ -1054,10 +1059,40 @@
 			return '';
 		} else {
 			return '
-				<span class="feedback">'.strip_tags($feedback, '<br>').'</span>';
+			<div class="feedback">'.strip_tags($feedback, '<br>').'</div>';
 		}
 	}
+	/**
+	 * warning_msg() - returns the htmlized warning string when an action is performed.
+	 *
+	 * @param string msg string
+	 * @return string htmlized warning
+	 */
+	function warning_msg($msg) {
+		if (!$msg) {
+			return '';
+		} else {
+			return '
+			<div class="warning_msg">'.strip_tags($msg, '<br>').'</div>';
+		}
+	}
+	
+	/**
+	 * error_msg() - returns the htmlized error string when an action is performed.
+	 *
+	 * @param string msg string
+	 * @return string htmlized error
+	 */
+	function error_msg($msg) {
+		if (!$msg) {
+			return '';
+		} else {
+			return '
+			<div class="error">'.strip_tags($msg, '<br>').'</div>';
+		}
+	}
 
+
 	/**
 	 * getThemeIdFromName()
 	 *

Modified: trunk/gforge/www/include/html.php
===================================================================
--- trunk/gforge/www/include/html.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/include/html.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -19,6 +19,39 @@
 }
 
 /**
+ * html_warning_top() - Show the warning output at the top of the page.
+ *
+ * @param		string	The warning message.
+ */
+function html_warning_top($msg) {
+	global $HTML;
+	echo $HTML->warning_msg($msg);
+}
+
+/**
+ * html_error_top() - Show the error output at the top of the page.
+ *
+ * @param		string	The error message.
+ */
+function html_error_top($msg) {
+	global $HTML;
+	echo $HTML->error_msg($msg);
+}
+
+/**
+ * make_user_link() - Make a username reference into a link to that users User page on SF.
+ *
+ * @param		string	The username of the user to link.
+ */
+function make_user_link($username) {
+	if (!strcasecmp($username,'Nobody') || !strcasecmp($username,'None')) {
+		return $username;
+	} else {
+		return '<a href="/users/'.$username.'">'.$username.'</a>' ;
+	}
+}
+
+/**
  * html_feedback_top() - Show the feedback output at the bottom of the page.
  *
  * @param		string	The feedback.
@@ -130,7 +163,13 @@
  */
 function html_get_theme_popup ($title='theme_id',$selected='xzxz') {
 	$res=db_query("SELECT theme_id, fullname FROM themes WHERE enabled=true");
-	return html_build_select_box($res,$title,$selected,false);
+	$nbTheme = db_numrows($res);
+	if($nbTheme < 2) {
+		return("");
+	}
+	else {
+		return html_build_select_box($res,$title,$selected,false);
+	}
 }
 
 /**
@@ -250,12 +289,12 @@
 	//we don't always want the default Any row shown
 	if ($show_any) {
 		$return .= '
-		<input type="radio" name="'.$select_name.'" value=""'.(($checked_val=='') ? ' checked' : '').'>&nbsp;'. $text_any .'<br />';
+		<input type="radio" name="'.$select_name.'" value=""'.(($checked_val=='') ? ' checked="checked"' : '').' />&nbsp;'. $text_any .'<br />';
 	}
 	//we don't always want the default 100 row shown
 	if ($show_100) {
 		$return .= '
-		<input type="radio" name="'.$select_name.'" value="100"'.(($checked_val==100) ? ' checked' : '').'>&nbsp;'. $text_100 .'<br />';
+		<input type="radio" name="'.$select_name.'" value="100"'.(($checked_val==100) ? ' checked="checked"' : '').' />&nbsp;'. $text_100 .'<br />';
 	}
 
 	$checked_found=false;
@@ -268,9 +307,9 @@
 				<input type="radio" name="'.$select_name.'" value="'.$vals[$i].'"';
 			if ((string)$vals[$i] == (string)$checked_val) {
 				$checked_found=true;
-				$return .= ' checked';
+				$return .= ' checked="checked"';
 			}
-			$return .= '>&nbsp;'.htmlspecialchars($texts[$i]).'<br />';
+			$return .= ' />&nbsp;'.htmlspecialchars($texts[$i]).'<br />';
 		}
 	}
 	//
@@ -279,7 +318,7 @@
 	//
 	if (!$checked_found && $checked_val != 'xzxz' && $checked_val && $checked_val != 100) {
 		$return .= '
-		<input type="radio" value="'.$checked_val.'" checked>&nbsp;'._('No Change').'<br />';
+		<input type="radio" value="'.$checked_val.'" checked="checked" />&nbsp;'._('No Change').'<br />';
 	}
 
 	return $return;
@@ -301,8 +340,9 @@
  * @param		string	What to call the '100 row' defaults to none
  * @param		bool	Whether or not to show the 'Any row'
  * @param		string	What to call the 'Any row' defaults to any
+ * @param		array	Array of all allowed values from the full list.
  */
-function html_build_select_box_from_arrays ($vals,$texts,$select_name,$checked_val='xzxz',$show_100=true,$text_100='none',$show_any=false,$text_any='any') {
+function html_build_select_box_from_arrays ($vals,$texts,$select_name,$checked_val='xzxz',$show_100=true,$text_100='none',$show_any=false,$text_any='any', $allowed=false) {
 	if ($text_100=='none'){
 		$text_100=_('None');
 	}
@@ -335,10 +375,13 @@
 		if (($vals[$i] != '100') || ($vals[$i] == '100' && !$show_100)) {
 			$return .= '
 				<option value="'.$vals[$i].'"';
-			if ($vals[$i] == $checked_val) {
+			if ((string)$vals[$i] == (string)$checked_val) {
 				$checked_found=true;
 				$return .= ' selected="selected"';
 			}
+			if (is_array($allowed) && !in_array($vals[$i], $allowed)) {
+				$return .= ' disabled="disabled" class="option_disabled"';
+			}
 			$return .= '>'./*htmlspecialchars(*/$texts[$i]/*)*/.'</option>';
 		}
 	}
@@ -366,13 +409,36 @@
  * @param		bool	Whether or not to show the '100 row'
  * @param		string	What to call the '100 row'.  Defaults to none.
  */
-function html_build_select_box ($result, $name, $checked_val="xzxz",$show_100=true,$text_100='none') {
+function html_build_select_box ($result, $name, $checked_val="xzxz",$show_100=true,$text_100='none',$show_any=false,$text_any='Select One') {
 	if ($text_100=='none'){
 		$text_100=_('None');
 	}
-	return html_build_select_box_from_arrays (util_result_column_to_array($result,0),util_result_column_to_array($result,1),$name,$checked_val,$show_100,$text_100);
+	return html_build_select_box_from_arrays (util_result_column_to_array($result,0),util_result_column_to_array($result,1),$name,$checked_val,$show_100,$text_100, $show_any, $text_any);
 }
+
 /**
+ * html_build_select_box_sorted() - Takes a result set, with the first column being the "id" or value and
+ * the second column being the text you want displayed.
+ *
+ * @param		int		The result set
+ * @param		string	Text to be displayed
+ * @param		string	The item that should be checked
+ * @param		bool	Whether or not to show the '100 row'
+ * @param		string	What to call the '100 row'.  Defaults to none.
+ */
+function html_build_select_box_sorted ($result, $name, $checked_val="xzxz",$show_100=true,$text_100='none') {
+	global $Language;
+	if ($text_100=='none'){
+		$text_100=_('None');
+	}
+	$vals = util_result_column_to_array($result, 0);
+	$texts = util_result_column_to_array($result, 1);
+	array_multisort($texts, SORT_ASC, SORT_STRING,
+	                $vals);
+	return html_build_select_box_from_arrays ($vals, $texts, $name, $checked_val, $show_100, $text_100);
+}
+
+/**
  * html_build_multiple_select_box() - Takes a result set, with the first column being the "id" or value
  * and the second column being the text you want displayed.
  *
@@ -595,6 +661,15 @@
 	if (!$project || !is_object($project)) {
 		exit_error("GROUP PROBLEM","PROBLEM CREATING GROUP OBJECT");
 	} else if ($project->isError()) {
+		if ($project->isPermissionDeniedError() && !session_get_user()) {
+ 			$next = '/account/login.php?feedback='.urlencode($project->getErrorMessage());
+ 			if (getStringFromServer('REQUEST_METHOD') != 'POST') {
+				$next .= '&return_to='.urlencode(getStringFromServer('REQUEST_URI'));
+ 			}
+			// @alu: change url.
+ 			header("Location: $next");
+ 			exit;
+		}
 		exit_error("Group Problem",$project->getErrorMessage());
 	}
 
@@ -617,7 +692,13 @@
 	}
 	echo $HTML->header($params);
 	
-	if(isset($GLOBALS['feedback'])) {
+	if(isset($GLOBALS['error_msg']) && $GLOBALS['error_msg']) {
+		echo html_error_top($GLOBALS['error_msg']);
+	}
+	if(isset($GLOBALS['warning_msg']) && $GLOBALS['warning_msg']) {
+		echo html_warning_top($GLOBALS['warning_msg']);
+	}
+	if(isset($GLOBALS['feedback']) && $GLOBALS['feedback']) {
 		echo html_feedback_top($GLOBALS['feedback']);
 	}
 //	echo $HTML->project_tabs($params['toptab'],$params['group'],$params['tabtext']);
@@ -632,10 +713,6 @@
  */
 function site_project_footer($params) {
 	GLOBAL $HTML;
-
-	if(isset($GLOBALS['feedback'])) {
-		echo html_feedback_bottom($GLOBALS['feedback']);
-	}
 	echo $HTML->footer($params);
 }
 

Added: trunk/gforge/www/js/common.js
===================================================================
--- trunk/gforge/www/js/common.js	                        (rev 0)
+++ trunk/gforge/www/js/common.js	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,59 @@
+
+function admin_window(adminurl) {
+	AdminWin = window.open( adminurl, 'AdminWindow','scrollbars=yes,resizable=yes, toolbar=yes, height=400, width=400, top=2, left=2');
+	AdminWin.focus();
+}
+
+function help_window(helpurl) {
+	HelpWin = window.open( helpurl,'HelpWindow','scrollbars=yes,resizable=yes,toolbar=no,height=400,width=600');
+}
+
+function MM_goToURL() { //v3.0
+	var i, args=MM_goToURL.arguments; document.MM_returnValue = false;
+	for (i=0; i<(args.length-1); i+=2) eval(args[i]+".location='"+args[i+1]+"'");
+}
+
+function toggledisplay(a,list) {
+  var elem=document.getElementById(list)
+  var open='/images/folderArrowOpen.png'
+  var close='/images/folderArrowClosed.png'
+  if (elem.style.display=='none') {
+    elem.style.display='block'
+    a.title='Click to display only admins'
+    a.src = open
+  } else {
+    elem.style.display='none';
+    a.title='Click to display all members'
+    a.src = close
+  }
+}
+
+function switch2edit (a,show,edit) {
+  var elemshow=document.getElementById(show)
+  var elemedit=document.getElementById(edit)
+  if (elemedit.style.display=='none') {
+    elemedit.style.display='block'
+    elemshow.style.display='none'
+    a.style.display='none'
+  }
+ }
+
+ function switch2display (a,bt,disp,i) {
+  var elembt1=document.getElementById(bt+'1_'+i)
+  var elemdisp1=document.getElementById(disp+'1_'+i)
+  var elembt2=document.getElementById(bt+'2_'+i)
+  var elemdisp2=document.getElementById(disp+'2_'+i)
+  
+  if (elemdisp1.style.display=='none') {
+    elembt1.style.display='inline'
+    elemdisp1.style.display='block'
+    elembt2.style.display='none'
+    elemdisp2.style.display='none'
+  }
+  else {
+    elembt1.style.display='none'
+    elemdisp1.style.display='none'
+    elembt2.style.display='inline'
+    elemdisp2.style.display='block'
+  }
+}

Added: trunk/gforge/www/js/sortable.js
===================================================================
--- trunk/gforge/www/js/sortable.js	                        (rev 0)
+++ trunk/gforge/www/js/sortable.js	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,325 @@
+/*
+Table sorting script  by Joost de Valk, check it out at http://www.joostdevalk.nl/code/sortable-table/.
+Based on a script from http://www.kryogenix.org/code/browser/sorttable/.
+Distributed under the MIT license: http://www.kryogenix.org/code/browser/licence.html .
+
+Copyright (c) 1997-2007 Stuart Langridge, Joost de Valk.
+
+Version 1.5.7
+*/
+
+/* You can change these values */
+var image_path = "/images/";
+var image_up = "sort_up.gif";
+var image_down = "sort_down.gif";
+var image_none = "sort_none.gif";
+var europeandate = true;
+var alternate_row_colors = true;
+
+/* Don't change anything below this unless you know what you're doing */
+addEvent(window, "load", sortables_init);
+
+var SORT_COLUMN_INDEX;
+var thead = false;
+
+function sortables_init() {
+	// Find all tables with class sortable and make them sortable
+	if (!document.getElementsByTagName) return;
+	tbls = document.getElementsByTagName("table");
+	for (ti=0;ti<tbls.length;ti++) {
+		thisTbl = tbls[ti];
+		if (((' '+thisTbl.className+' ').indexOf("sortable") != -1) && (thisTbl.id)) {
+			ts_makeSortable(thisTbl);
+		}
+	}
+}
+
+function ts_makeSortable(t) {
+	if (t.rows && t.rows.length > 0) {
+		if (t.tHead && t.tHead.rows.length > 0) {
+			var firstRow = t.tHead.rows[t.tHead.rows.length-1];
+			thead = true;
+		} else {
+			var firstRow = t.rows[0];
+		}
+	}
+	if (!firstRow) return;
+	
+	// We have a first row: assume it's the header, and make its contents clickable links
+	for (var i=0;i<firstRow.cells.length;i++) {
+		var cell = firstRow.cells[i];
+		var txt = ts_getInnerText(cell);
+		if (cell.className != "unsortable" && cell.className.indexOf("unsortable") == -1) {
+			cell.innerHTML = '<a href="#" class="sortheader" onclick="ts_resortTable(this, '+i+');return false;">'+txt+'<span class="sortarrow">&nbsp;&nbsp;<img src="'+ image_path + image_none + '" alt="&darr;"/></span></a>';
+		}
+	}
+	if (alternate_row_colors) {
+		alternate(t);
+	}
+}
+
+function ts_getInnerText(el) {
+	if (typeof el == "string") return el;
+	if (typeof el == "undefined") { return el };
+	if (el.innerText) return el.innerText;	//Not needed but it is faster
+	var str = "";
+	
+	var cs = el.childNodes;
+	var l = cs.length;
+	for (var i = 0; i < l; i++) {
+		switch (cs[i].nodeType) {
+			case 1: //ELEMENT_NODE
+				str += ts_getInnerText(cs[i]);
+				break;
+			case 3:	//TEXT_NODE
+				str += cs[i].nodeValue;
+				break;
+		}
+	}
+	return str;
+}
+
+function ts_resortTable(lnk, clid) {
+	var span;
+	for (var ci=0;ci<lnk.childNodes.length;ci++) {
+		if (lnk.childNodes[ci].tagName && lnk.childNodes[ci].tagName.toLowerCase() == 'span') span = lnk.childNodes[ci];
+	}
+	var spantext = ts_getInnerText(span);
+	var td = lnk.parentNode;
+	var column = clid || td.cellIndex;
+	var t = getParent(td,'TABLE');
+	// Work out a type for the column
+	if (t.rows.length <= 1) return;
+	var itm = "";
+	var i = 0;
+	while (itm == "" && i < t.tBodies[0].rows.length) {
+		var itm = ts_getInnerText(t.tBodies[0].rows[i].cells[column]);
+		itm = trim(itm);
+		if (itm.substr(0,4) == "<!--" || itm.length == 0) {
+			itm = "";
+		}
+		i++;
+	}
+	if (itm == "") return; 
+	sortfn = ts_sort_caseinsensitive;
+	if (itm.match(/^\d\d[\/\.-][a-zA-z][a-zA-Z][a-zA-Z][\/\.-]\d\d\d\d$/)) sortfn = ts_sort_date;
+	if (itm.match(/^\d\d[\/\.-]\d\d[\/\.-]\d\d\d{2}?$/)) sortfn = ts_sort_date;
+	if (itm.match(/^-?[£$€Û¢´]\d/)) sortfn = ts_sort_numeric;
+	// ape: added to provide numeric sort on size for the docs tools.
+	if (itm.match(/^\d+ *(B|KB|MB)$/)) sortfn = ts_sort_numeric;
+	if (itm.match(/^-?(\d+[,\.]?)+(E[-+][\d]+)?%?$/)) sortfn = ts_sort_numeric;
+	SORT_COLUMN_INDEX = column;
+	var firstRow = new Array();
+	var newRows = new Array();
+	for (k=0;k<t.tBodies.length;k++) {
+		for (i=0;i<t.tBodies[k].rows[0].length;i++) { 
+			firstRow[i] = t.tBodies[k].rows[0][i]; 
+		}
+	}
+	for (k=0;k<t.tBodies.length;k++) {
+		if (!thead) {
+			// Skip the first row
+			for (j=1;j<t.tBodies[k].rows.length;j++) { 
+				newRows[j-1] = t.tBodies[k].rows[j];
+			}
+		} else {
+			// Do NOT skip the first row
+			for (j=0;j<t.tBodies[k].rows.length;j++) { 
+				newRows[j] = t.tBodies[k].rows[j];
+			}
+		}
+	}
+	newRows.sort(sortfn);
+	if (span.getAttribute("sortdir") == 'down') {
+			ARROW = '&nbsp;&nbsp;<img src="'+ image_path + image_down + '" alt="&darr;"/>';
+			newRows.reverse();
+			span.setAttribute('sortdir','up');
+	} else {
+			ARROW = '&nbsp;&nbsp;<img src="'+ image_path + image_up + '" alt="&uarr;"/>';
+			span.setAttribute('sortdir','down');
+	} 
+    // We appendChild rows that already exist to the tbody, so it moves them rather than creating new ones
+    // don't do sortbottom rows
+    for (i=0; i<newRows.length; i++) { 
+		if (!newRows[i].className || (newRows[i].className && (newRows[i].className.indexOf('sortbottom') == -1))) {
+			t.tBodies[0].appendChild(newRows[i]);
+		}
+	}
+    // do sortbottom rows only
+    for (i=0; i<newRows.length; i++) {
+		if (newRows[i].className && (newRows[i].className.indexOf('sortbottom') != -1)) 
+			t.tBodies[0].appendChild(newRows[i]);
+	}
+	// Delete any other arrows there may be showing
+	var allspans = document.getElementsByTagName("span");
+	for (var ci=0;ci<allspans.length;ci++) {
+		if (allspans[ci].className == 'sortarrow') {
+			if (getParent(allspans[ci],"table") == getParent(lnk,"table")) { // in the same table as us?
+				allspans[ci].innerHTML = '&nbsp;&nbsp;<img src="'+ image_path + image_none + '" alt="&darr;"/>';
+			}
+		}
+	}		
+	span.innerHTML = ARROW;
+	alternate(t);
+}
+
+function getParent(el, pTagName) {
+	if (el == null) {
+		return null;
+	} else if (el.nodeType == 1 && el.tagName.toLowerCase() == pTagName.toLowerCase()) {
+		return el;
+	} else {
+		return getParent(el.parentNode, pTagName);
+	}
+}
+
+function sort_date(date) {	
+	// y2k notes: two digit years less than 50 are treated as 20XX, greater than 50 are treated as 19XX
+	dt = "00000000";
+	if (date.length == 11) {
+		mtstr = date.substr(3,3);
+		mtstr = mtstr.toLowerCase();
+		switch(mtstr) {
+			case "jan": var mt = "01"; break;
+			case "feb": var mt = "02"; break;
+			case "mar": var mt = "03"; break;
+			case "apr": var mt = "04"; break;
+			case "may": var mt = "05"; break;
+			case "jun": var mt = "06"; break;
+			case "jul": var mt = "07"; break;
+			case "aug": var mt = "08"; break;
+			case "sep": var mt = "09"; break;
+			case "oct": var mt = "10"; break;
+			case "nov": var mt = "11"; break;
+			case "dec": var mt = "12"; break;
+			// default: var mt = "00";
+		}
+		dt = date.substr(7,4)+mt+date.substr(0,2);
+		return dt;
+	} else if (date.length == 10) {
+		if (europeandate == false) {
+			dt = date.substr(6,4)+date.substr(0,2)+date.substr(3,2);
+			return dt;
+		} else {
+			dt = date.substr(6,4)+date.substr(3,2)+date.substr(0,2);
+			return dt;
+		}
+	} else if (date.length == 8) {
+		yr = date.substr(6,2);
+		if (parseInt(yr) < 50) { 
+			yr = '20'+yr; 
+		} else { 
+			yr = '19'+yr; 
+		}
+		if (europeandate == true) {
+			dt = yr+date.substr(3,2)+date.substr(0,2);
+			return dt;
+		} else {
+			dt = yr+date.substr(0,2)+date.substr(3,2);
+			return dt;
+		}
+	}
+	return dt;
+}
+
+function ts_sort_date(a,b) {
+	dt1 = sort_date(ts_getInnerText(a.cells[SORT_COLUMN_INDEX]));
+	dt2 = sort_date(ts_getInnerText(b.cells[SORT_COLUMN_INDEX]));
+	
+	if (dt1==dt2) {
+		return 0;
+	}
+	if (dt1<dt2) { 
+		return -1;
+	}
+	return 1;
+}
+function ts_sort_numeric(a,b) {
+	var aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
+	aa = clean_num(aa);
+	var bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
+	bb = clean_num(bb);
+	return compare_numeric(aa,bb);
+}
+function compare_numeric(a,b) {
+	var a = parseFloat(a);
+	a = (isNaN(a) ? 0 : a);
+	var b = parseFloat(b);
+	b = (isNaN(b) ? 0 : b);
+	return a - b;
+}
+function ts_sort_caseinsensitive(a,b) {
+	aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]).toLowerCase();
+	bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]).toLowerCase();
+	if (aa==bb) {
+		return 0;
+	}
+	if (aa<bb) {
+		return -1;
+	}
+	return 1;
+}
+function ts_sort_default(a,b) {
+	aa = ts_getInnerText(a.cells[SORT_COLUMN_INDEX]);
+	bb = ts_getInnerText(b.cells[SORT_COLUMN_INDEX]);
+	if (aa==bb) {
+		return 0;
+	}
+	if (aa<bb) {
+		return -1;
+	}
+	return 1;
+}
+function addEvent(elm, evType, fn, useCapture)
+// addEvent and removeEvent
+// cross-browser event handling for IE5+,	NS6 and Mozilla
+// By Scott Andrew
+{
+	if (elm.addEventListener){
+		elm.addEventListener(evType, fn, useCapture);
+		return true;
+	} else if (elm.attachEvent){
+		var r = elm.attachEvent("on"+evType, fn);
+		return r;
+	} else {
+		alert("Handler could not be removed");
+	}
+}
+function clean_num(str) {
+	str = str.replace(new RegExp(/[^-?0-9.]/g),"");
+	return str;
+}
+function trim(s) {
+	return s.replace(/^\s+|\s+$/g, "");
+}
+function alternate(table) {
+	// Take object table and get all it's tbodies.
+	var tableBodies = table.getElementsByTagName("tbody");
+	// Loop through these tbodies
+	for (var i = 0; i < tableBodies.length; i++) {
+		// Take the tbody, and get all it's rows
+		var tableRows = tableBodies[i].getElementsByTagName("tr");
+		// Loop through these rows
+		// Start at 1 because we want to leave the heading row untouched
+		for (var j = 0; j < tableRows.length; j++) {
+			// Check if j is even, and apply classes for both possible results
+			if ( (j % 2) == 0  ) {
+				if ( !(tableRows[j].className.indexOf('odd') == -1) ) {
+					tableRows[j].className = tableRows[j].className.replace('odd', 'even');
+				} else {
+					if ( tableRows[j].className.indexOf('even') == -1 ) {
+						tableRows[j].className += " even";
+					}
+				}
+			} else {
+				if ( !(tableRows[j].className.indexOf('even') == -1) ) {
+					tableRows[j].className = tableRows[j].className.replace('even', 'odd');
+				} else {
+					if ( tableRows[j].className.indexOf('odd') == -1 ) {
+						tableRows[j].className += " odd";
+					}
+				}
+			} 
+		}
+	}
+}

Modified: trunk/gforge/www/people/admin/index.php
===================================================================
--- trunk/gforge/www/people/admin/index.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/people/admin/index.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -136,7 +136,7 @@
 		<span class="imporant">Once you add a category, it cannot be deleted</span></p>
 		<p>
 		<input type="submit" name="submit" value="SUBMIT"></p>
-		</form></p>
+		</form>
 		<?php
 
 		people_footer(array());

Modified: trunk/gforge/www/themes/gforge/Theme.class.php
===================================================================
--- trunk/gforge/www/themes/gforge/Theme.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/themes/gforge/Theme.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -113,19 +113,11 @@
     <title><?php echo $params['title']; ?></title>
     <link rel="icon" type="image/png" href="<?php echo util_make_url('/images/icon.png'); ?>"/>
     <link rel="shortcut icon" type="image/png" href="<?php echo util_make_url('/images/icon.png'); ?>"/>
-		<?php $this->headerLink(); ?>
-    <script language="JavaScript" type="text/javascript">
-    <!--
-
-    function admin_window(adminurl) {
-        AdminWin = window.open( adminurl, 'AdminWindow','scrollbars=yes,resizable=yes, toolbar=yes, height=400, width=400, top=2, left=2');
-        AdminWin.focus();
-    }
-    function help_window(helpurl) {
-        HelpWin = window.open( helpurl,'HelpWindow','scrollbars=yes,resizable=yes,toolbar=no,height=400,width=600');
-    }
-    // -->
-<?php plugin_hook ("javascript",false) ; ?>
+	<?php $this->headerLink(); ?>
+    <script type="text/javascript" src="<?php echo util_make_url('/js/common.js'); ?>"></script>
+    <script type="text/javascript" src="<?php echo util_make_url('/js/sortable.js'); ?>"></script>
+    <script type="text/javascript">
+    <?php plugin_hook ("javascript",false) ; ?>
     </script>
 <?php
 	      if (_('default_font') != 'default_font') {

Added: trunk/gforge/www/themes/gforge/images/ic/acl_roles20.png
===================================================================
--- trunk/gforge/www/themes/gforge/images/ic/acl_roles20.png	                        (rev 0)
+++ trunk/gforge/www/themes/gforge/images/ic/acl_roles20.png	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,16 @@
+‰PNG
+
+   
+IHDR         ‰
+   bKGD ÿ ÿ ÿ ½§“   	pHYs     šœ   tIMEØ
+'²ƒb  øIDAT8˝”_h•uÇ?¿÷=ç̳vþÌMÝœ±ÍµÜØX1eM¢„Ò‚AÖEtuiAuÓMB	Ñ” ôª.
+$‚)j™éÄiMÅmgž¹ã9;gç¼ç}Ïûû×…'ƒ$|àÏ>|Ÿ‡_Áÿ¬þþ¾®í£Û^à»9*–Š¹Ã_<r‘{©t:Ý}äÈ7çó¹yú9{+;e½ïÖ[oŽÞÏyïÝ·UË9«Â²UAÎË×l%÷»ß¿÷ØÇï¿Ñ¸r8ò!à ¸Ô¿eóöé‰O¬ª–…¶QÐQW140ºõó“g~½pøH×ßvüÀ!öíÙ ÖGá ¥d±²¡w´¡££}ëJ ³
+Ö|º ~9}V|ÿÓ9t C«Büj œ·ñw[¹{}Âíxí±$ó%C90t6»äÊŠ‰E^}2¥5ÙB„ŠÚ¥bù®7lzv žI‰¸XaM2™	?íq«ä“Š»,yŠŸ'—؝ðfæ检 ,€»ÚáØpãžÎµ¡”FIÖk,s!Sµ¢ØF%›¤ÙM³8=ÛprújWA)Wzá@­v˜UÊ,¾l™º©ð¥¥-ás,Ã}C¼œL±qaY© Cœ›¥54{›>8æÐê-TÞYíГÚ>Õ×êl>~µF{Bp|ºF¤évÅâ´ÎN#ýRšº4q©é_ª‰™Ž5Ì䉕À‘Þ¡‘ñõ?ÿø•ëóÑëÙ"¾‰ñLç ›æ.£¤Aª:LÝ*ep´%Y3q·D¼±i×α×ô?‘4Ö!¿8H§Ÿa¹p“uɺ.ÅK´}p«(ƒ£
+®6Xe/LÒ5;Gg l¤¥¥eó¶gw}Ö¶e$Yªø¸ŽKdM‚çžÞÍð@ß~qØ™âÑô|¸cÌi­1Æpí£½¿:@_¨³‘TkûÎô¦þ
+åJ×uq!àÔSô÷v1{#GÕ¯ÑP*á]¹Œµ •º
+Õ­4Õì•P“WƏxì©††ˆ
+ˆÔ ˜ñŠ\º–á†n 
+֝:ÆÅÇ‘Ê ¤þ÷–Ò k’Òèó†Š+„3°vcÏŽh,Ž®¯¡•¦ê„R“YªRY˜¥Û+ B…Õí/sG
+O&”™9'Ü0¨æ3m•R!Y)å]¥
+RJø>~µ*
+Å¢‹¥ŒÈü%Út(”²HiPJ#¥Á“†SÊ”°ü(áODëaÐÜtÔûÆd2é.//+ ÒìóÒ}‘±µÉ”!#uø›²WNÂÑ &sbEê8u¸»Bÿ¤‘4 {:R0è[ÒËròÒ·M«ø»3    IEND®B`‚
\ No newline at end of file

Added: trunk/gforge/www/themes/gforge/images/ic/btn_down.png
===================================================================
--- trunk/gforge/www/themes/gforge/images/ic/btn_down.png	                        (rev 0)
+++ trunk/gforge/www/themes/gforge/images/ic/btn_down.png	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,6 @@
+‰PNG
+
+   
+IHDR         K"øZ   0PLTE   ÿÿÿYA›I/’P7–T<™ÿÿÿ                           Ú…‹é   /IDATxœc6“ UcӁ&Ð<(SÐØÆ4„«†3
+)²
+át ›$_m)    IEND®B`‚
\ No newline at end of file

Added: trunk/gforge/www/themes/gforge/images/ic/btn_up.png
===================================================================
--- trunk/gforge/www/themes/gforge/images/ic/btn_up.png	                        (rev 0)
+++ trunk/gforge/www/themes/gforge/images/ic/btn_up.png	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,5 @@
+‰PNG
+
+   
+IHDR         K"øZ   0PLTE   ÿÿÿYA›I/’P7–T<™ÿÿÿ                           Ú…‹é   2IDATxœc6“ UcӁd¦!‚)(c
+˜‚Ɔ"P&ÐBr¬Àd"œ ë$_â>õ    IEND®B`‚
\ No newline at end of file

Added: trunk/gforge/www/themes/gforge/images/spacer.gif
===================================================================
--- trunk/gforge/www/themes/gforge/images/spacer.gif	                        (rev 0)
+++ trunk/gforge/www/themes/gforge/images/spacer.gif	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,5 @@
+GIF89a
+ 
+ €  ÿÿÿ   !ù    ,    
+ 
+  „©Ëíc+ ;
\ No newline at end of file

Modified: trunk/gforge/www/tracker/add.php
===================================================================
--- trunk/gforge/www/tracker/add.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/add.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -29,17 +29,18 @@
 		Show the free-form text submitted by the project admin
 	*/
 	echo notepad_func();
-	echo $ath->getSubmitInstructions();
+	echo $ath->renderSubmitInstructions();
 
-	echo '<p>
-
-	<form action="'.getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&atid='.$ath->getID().'" method="post" enctype="multipart/form-data">
-	<input type="hidden" name="form_key" value="'.form_generate_key().'">
-	<input type="hidden" name="func" value="postadd" />
+	echo '<form action="'.getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;atid='.$ath->getID().'" method="post" enctype="multipart/form-data">
+	
+	<input type="hidden" name="MAX_FILE_SIZE" value="10000000" />
 	<table>';
 	echo '
 	<tr>
-		<td valign="top">';
+		<td valign="top">
+	            <input type="hidden" name="form_key" value="'.form_generate_key().'" />
+	            <input type="hidden" name="func" value="postadd" />
+	            <input type="hidden" name="MAX_FILE_SIZE" value="10000000" />';
 	if (!session_loggedin()) {
 		echo '
 		<span class="error">'.sprintf(_('Please %1$s login %2$s'), '<a href="'.util_make_url ('/account/login.php?return_to='.urlencode($REQUEST_URI)).'">', '</a>').'</span><<br />
@@ -55,8 +56,8 @@
 		<td valign="top"><input type="submit" name="submit" value="'. _('Submit').'" /></td>
 	</tr>';
 	
-	$ath->renderExtraFields(array(),true,'none');
- 
+	$ath->renderExtraFields(array(),true,'none',false,'Any','',false,'UPDATE');
+
 	if ($ath->userIsAdmin()) {
 		echo '<tr>
 		<td><strong>'._('Assigned to').': <a href="javascript:help_window(\''.util_make_url ('/help/tracker.php?helpname=assignee').'\')"><strong>(?)</strong></a></strong><br />';
@@ -70,16 +71,15 @@
 	
 	?>
 	<tr>
-		<td colspan="2"><strong><?php echo _('Summary') ?>: <a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=summary'); ?>')"></strong><?php echo utils_requiredField(); ?><strong>(?)</strong></a><br />
+		<td colspan="2"><strong><?php echo _('Summary') ?><?php echo utils_requiredField(); ?>: <a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=summary'); ?>')">(?)</a></strong><br />
 		<input type="text" name="summary" size="80" maxlength="255" />
 		</td>
 	</tr>
 
 	<tr>
 		<td colspan="2">
-		<strong><?php echo _('Detailed description') ?>:</strong><?php echo notepad_button('document.forms[1].details') ?> <?php echo utils_requiredField(); ?>
-		<p>
-		<textarea name="details" rows="30" cols="79"></textarea></p>
+		<strong><?php echo _('Detailed description') ?><?php echo utils_requiredField(); ?>:</strong><?php echo notepad_button('document.forms[1].details') ?><br /> 
+		<textarea name="details" rows="30" cols="79"></textarea>
 		</td>
 	</tr>
 
@@ -110,15 +110,17 @@
 		<input type="file" name="input_file[]" size="30" /><br />
 		<input type="file" name="input_file[]" size="30" /><br />
 		<input type="file" name="input_file[]" size="30" /><br />
+		</p>
 		</td>
-	<tr>
+	</tr>
 
 	<tr><td colspan="2">
 		<input type="submit" name="submit" value="<?php echo _('Submit')?>" />
 		</td>
 	</tr>
 
-	</table></form></p>
+	<tr><td colspan="2"><br/><?php echo utils_requiredField(); ?> <?php echo $Language->getText('general','required_fields')?></td></tr>
+	</table></form>
 
 	<?php
 

Modified: trunk/gforge/www/tracker/admin/form-addcanned.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-addcanned.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-addcanned.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -47,7 +47,7 @@
 		<input type="text" name="title" value="" size="50" maxlength="50" />
 		<p>
 		<strong><?php echo _('Message Body') ?>:</strong><br />
-		<textarea name="body" rows="30" cols="65" wrap="hard"></textarea></p>
+		<textarea name="body" rows="30" cols="65"></textarea></p>
 		<p>
 		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
 		</form>

Modified: trunk/gforge/www/tracker/admin/form-addextrafield.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-addextrafield.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-addextrafield.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -28,7 +28,7 @@
 			for ($k=0; $k < $rows; $k++) {
 				$i=$keys[$k];
 				echo '<tr '. $GLOBALS['HTML']->boxGetAltRowStyle($i) .'>'.
-					'<td>'.$efarr[$i]['field_name'].'<a href="'.getStringFromServer('PHP_SELF').'?update_box=1&amp;id='.
+					'<td>'.$efarr[$i]['field_name'].(($efarr[$i]['is_required']) ? utils_requiredField() : '').'<a href="'.getStringFromServer('PHP_SELF').'?update_box=1&amp;id='.
 						$efarr[$i]['extra_field_id'].'&amp;group_id='.$group_id.'&amp;atid='. $ath->getID() .'">'.
 						' ['._('Edit').']</a>'.
 					'<a href="'.getStringFromServer('PHP_SELF').'?deleteextrafield=1&amp;id='.
@@ -53,7 +53,7 @@
 						echo '<a href="'.getStringFromServer('PHP_SELF').'?update_opt=1&amp;id='.
 						$elearray[$j]['element_id'].'&amp;group_id='.$group_id.'&amp;atid='. $ath->getID() .'&amp;boxid='.
 						$efarr[$i]['extra_field_id'].'">'.
-						$elearray[$j]['element_name'].' ['._('Edit').']</a><br \>';
+						$elearray[$j]['element_name'].' ['._('Edit').']</a><br />';
 
 					} else {
 						echo '<td>';
@@ -71,37 +71,38 @@
 						_('add choices').']</a>';
 				}
 				echo '</td>'; 
+			        echo   '</tr>';
 			}
-			echo   '</tr>';
 			echo $GLOBALS['HTML']->listTableBottom();
-
+			
+			echo utils_requiredField().' '._('Indicates required fields.');
 		} else { 
 			echo "\n<h3>"._('You have not defined any custom fields')."</h3>";
 		}
 
 		echo "<h2>"._('Add New Custom Field')."</h2>";
 		?>
+		<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;atid='.$ath->getID(); ?>" method="post">
+		<input type="hidden" name="add_extrafield" value="y" />
 		<p>
-		<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&atid='.$ath->getID(); ?>" method="post">
-		<input type="hidden" name="add_extrafield" value="y" />
 		<strong><?php echo _('Custom Field Name') ?>:</strong><br />
 		<input type="text" name="name" value="" size="15" maxlength="30" /><br />
 		<p>
 		<strong><?php echo _('Field alias') ?>:</strong><br />
 		<input type="text" name="alias" value="" size="15" maxlength="30" /><br />
 		<p>
-
 		<strong><?php  echo _('Type of custom field') ?>:</strong><br />
-		<input type="radio" name="field_type" value="1"> <?php echo _('Select Box'); ?><br />
-		<input type="radio" name="field_type" value="2"> <?php echo _('Check Box'); ?><br />
-		<input type="radio" name="field_type" value="3"> <?php echo _('Radio Buttons'); ?><br />
-		<input type="radio" name="field_type" value="4"> <?php echo _('Text Field'); ?><br />
-		<input type="radio" name="field_type" value="5"> <?php echo _('Multi-Select Box'); ?><br />
-		<input type="radio" name="field_type" value="6"> <?php echo _('Text Area'); ?><br />
+		<input type="radio" name="field_type" value="1" /> <?php echo _('Select Box'); ?><br />
+		<input type="radio" name="field_type" value="2" /> <?php echo _('Check Box'); ?><br />
+		<input type="radio" name="field_type" value="3" /> <?php echo _('Radio Buttons'); ?><br />
+		<input type="radio" name="field_type" value="4" /> <?php echo _('Text Field'); ?><br />
+		<input type="radio" name="field_type" value="5" /> <?php echo _('Multi-Select Box'); ?><br />
+		<input type="radio" name="field_type" value="6" /> <?php echo _('Text Area'); ?><br />
 		<?php if (!$ath->usesCustomStatuses()) { ?>
-		<input type="radio" name="field_type" value="7"> <?php echo _('Status'); ?><br />
+		<input type="radio" name="field_type" value="7" /> <?php echo _('Status'); ?><br />
 		<?php } ?>
-		<!--<input type="radio" name="field_type" value="8"> <?php echo _('Box type technician'); ?><br />-->
+		<!--<input type="radio" name="field_type" value="8" /> <?php echo _('Box type technician'); ?><br />-->
+		<input type="radio" name="field_type" value="9" /> <?php echo _('Relation between artifacts'); ?><br />
 		<p>
 		<?php echo _('Text Fields and Text Areas need to have Size/Maxlength and Rows/Cols defined, respectively.'); ?><br />
 		<?php echo _('Text Field Size/Text Area Rows'); ?>
@@ -109,17 +110,21 @@
 		<?php echo _('Text Field Maxlength/Text Area Columns'); ?>
 			<input type="text" name="attribute2" value="0" size="2" maxlength="2">
 		<p>
-		<span class="warning"><?php echo _('Warning: this add new custom field') ?></span></p>
+		<span class="warning"><?php echo _('Warning: this add new custom field') ?></span>
+		</p>
 		<p>
-		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
-		</form></p>
+		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" />
+		</p>
+		</form>
 		<?php
 
 		echo "<h2>"._('Custom Field Rendering Template')."</h2><p>";
 
+		echo "<p>";
 		echo '<a href="'.getStringFromServer('PHP_SELF').'?downloadtemplate=1&amp;group_id='.$group_id.'&amp;atid='. $ath->getID() .'">'._('Download default template').'</a><br />';
 		echo '<a href="'.getStringFromServer('PHP_SELF').'?uploadtemplate=1&amp;group_id='.$group_id.'&amp;atid='. $ath->getID() .'">'._('Add/Update template').'</a><br />';
 		echo '<a href="'.getStringFromServer('PHP_SELF').'?deletetemplate=1&amp;group_id='.$group_id.'&amp;atid='. $ath->getID() .'">'._('Delete template').'</a><br />';
+		echo "</p>";
 
 		$ath->footer(array());
 

Modified: trunk/gforge/www/tracker/admin/form-addextrafieldoption.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-addextrafieldoption.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-addextrafieldoption.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -19,29 +19,61 @@
 		$rows=count($efearr);
 		if ($rows > 0) {
 			
+			?>
+			<form action="<?php echo 'index.php?group_id='.$group_id.'&amp;atid='.$ath->getID().'&amp;boxid='.$boxid; ?>" method="post">
+			<?php
 			$title_arr=array();
+			$title_arr[]=_('Current / New positions');
+			$title_arr[]=_('Up/Down positions');
 			$title_arr[]=_('Elements Defined');
+			$title_arr[]='';
+			
+			echo $GLOBALS['HTML']->listTableTop ($title_arr,false, ' ');
 
-			echo $GLOBALS['HTML']->listTableTop ($title_arr);
-
 			for ($i=0; $i < $rows; $i++) {
 				echo '<tr '. $GLOBALS['HTML']->boxGetAltRowStyle($i) .'>'.
-					'<td><a href="'.getStringFromServer('PHP_SELF').'?update_opt=1&amp;id='.
+					'<td align="right">'.
+					($i + 1).'&nbsp;--&gt;&nbsp;<input type="text" name="order['. $efearr[$i]['element_id'] .']" value="" size="3" maxlength="3" />'.
+					'</td>'."\n".'<td align="center">'.'&nbsp;&nbsp;&nbsp;'.
+					'<a href="index.php?group_id='.$group_id.'&amp;atid='.$ath->getID().'&amp;boxid='.$boxid.'&amp;id='.$efearr[$i]['element_id'].
+					'&amp;updownorder_opt=1&amp;new_pos='.(($i == 0)? $i + 1 : $i).'">'.html_image('ic/btn_up.png','19','18',array('alt'=>"Up")).'</a>'.
+					'&nbsp;&nbsp;'.
+					'<a href="index.php?group_id='.$group_id.'&amp;atid='.$ath->getID().'&amp;boxid='.$boxid.'&amp;id='.$efearr[$i]['element_id'].
+					'&amp;updownorder_opt=1&amp;new_pos='.(($i == $rows - 1)? $i + 1 : $i + 2).'">'.html_image('ic/btn_down.png','19','18',array('alt'=>"Down")).'</a>'.
+					'</td>'."\n".'<td>'.'&nbsp;&nbsp;&nbsp;'.$efearr[$i]['element_name'].
+					'</td>'."\n".'<td align="center">'.
+					'<a href="'.getStringFromServer('PHP_SELF').'?update_opt=1&amp;id='.
 					$efearr[$i]['element_id'].'&amp;boxid='.			
 					$boxid.'&amp;group_id='.$group_id.'&amp;atid='. $ath->getID() .'">'.
-					$efearr[$i]['element_name'].' ['._('Edit').']</a></td>';
+					html_image('ic/forum_edit.gif','37','15',array('alt'=>"Edit")).'</a>'.
+					'</td></tr>'."\n";
 			}		   
-			echo $GLOBALS['HTML']->listTableBottom();
+//			echo $GLOBALS['HTML']->listTableBottom();
+			?>
+			<tr class="noborder">
+			<td align="right">
+			<input type="submit" name="post_changes_order" value="<?php echo _('Reorder') ?>" />
+			</td>
+			<td>
+			</td>
+			<td align="left">
+			<input type="submit" name="post_changes_alphaorder" value="<?php echo _('Alphabetical order') ?>" />
+			</td>
+			</tr>
+			<?php echo $GLOBALS['HTML']->listTableBottom(); ?>
+			</form>
+			<?php
 
 		} else { 
 			echo "\n<h3>"._('You have not defined any elements')."</h3>";
 		}
 		?>
+		<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;boxid='.$boxid.'&amp;atid='.$ath->getID(); ?>" method="post">
+		<input type="hidden" name="add_opt" value="y" />
+		<br />
 		<p>
-		<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&boxid='.$boxid.'&atid='.$ath->getID(); ?>" method="post">
-		<input type="hidden" name="add_opt" value="y" />
-		<strong><?php echo _('Add New Element') ?>:</strong><br />
-		<input type="text" name="name" value="" size="15" maxlength="30" /> <br \>
+		<strong><?php echo _('Add New Element') ?>:</strong>
+		<input type="text" name="name" value="" size="15" maxlength="30" />
 		<!--
 		Show a pop-up box to choose the possible statuses that this element will map to
 		-->
@@ -49,12 +81,11 @@
 		<strong><?php echo _('Status'); ?></strong><br />
 		<?php echo $ath->statusBox('status_id',1,false,false); ?>
 		<?php } ?>
-		<p>
-		<span class="warning"><?php echo _('Once you add a new element, it cannot be deleted') ?></span></p>
-		<p>
-		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
+		&nbsp;&nbsp;<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" />
+		</p>
+		<span class="warning"><?php echo _('Once you add a new element, it cannot be deleted') ?></span>
 		</form>
-		</p>
+		<br />
 		<?php
 		$ath->footer(array());
 	}

Added: trunk/gforge/www/tracker/admin/form-customizelist.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-customizelist.php	                        (rev 0)
+++ trunk/gforge/www/tracker/admin/form-customizelist.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,69 @@
+<?php
+//
+//	FORM TO UPDATE ARTIFACT TYPES
+//
+		$ath->adminHeader(array ('title'=>_('Customize Browse List'),'pagename'=>'tracker_admin_customize_liste','titlevals'=>array($ath->getName())));
+
+		/*
+			List of possible user built Selection Boxes for an ArtifactType
+		*/
+		$efarr =& $ath->getExtraFields();
+		
+		$browse_fields = explode(',',$ath->getBrowseList());
+		?>
+
+		<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;atid='.$ath->getID(); ?>" method="post">
+		<input type="hidden" name="customize_list" value="y" />
+		<p>
+		<?php echo _('Set order of the fields that will be displayed on the browse view of your tracker:') ?>
+		</p>
+		
+		<?php
+		// Display regular fields.
+		$fields = array (
+			'summary' => _('Summary'),
+			'open_date' => _('Open Date'),
+			'status_id' => _('State'),
+			'priority'  => _('Priority'),
+			'assigned_to' => _('Assigned To'),
+			'submitted_by' => _('Submitted By'),
+			'close_date' => _('Close Date'),
+			'details' => _('Detailed description')
+			);
+
+    if(count($ath->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_STATUS)) > 0) {
+      unset($fields['status_id']);
+    }
+
+		foreach ($fields as $f => $name) {
+			$pos = array_search($f, $browse_fields);
+			echo "<input type=\"text\" name=\"browse_fields[$f]\" value=\"" .
+				 (($pos !== false) ? $pos + 1 : '') .
+				 "\" size=\"3\" maxlength=\"3\" />" .
+				 $name .
+				 "<br />\n";
+		}	
+		
+		$keys=array_keys($efarr);
+		$rows=count($keys);
+		if ($rows > 0) {
+			for ($k=0; $k < $rows; $k++) {
+				$i=$keys[$k];
+				$pos = array_search($i, $browse_fields);
+				echo "<input type=\"text\" name=\"browse_fields[$i]\" value=\"" .
+				 	 (($pos !== false) ? $pos + 1 : '') .
+				 	 "\" size=\"3\" maxlength=\"3\" />" .
+					 $efarr[$i]['field_name'] .
+					 "<br />\n";
+			}
+		}
+		?>
+
+		<p>
+		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
+		</form>
+		<?php
+
+		$ath->footer(array());
+
+?>

Modified: trunk/gforge/www/tracker/admin/form-extrafieldcopy.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-extrafieldcopy.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-extrafieldcopy.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -38,30 +38,26 @@
 			$field_arr[] = $efearr[$i]['element_name'];
 		}
 		?>
-		<form action="<?php echo getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&amp;atid='.$ath->getID(); ?>" method="post" >
-                <?php
-		echo '<table>';
-		echo '<tr>';
-		echo '<td></td><td><center><strong>';
-		echo _('Copy From');
-		echo '<br />';
-		echo $fb->getName();
-		echo '</strong></center></td><td></td><td><center><strong>';
-		
-		echo _('Into trackers and custom fields');
-		echo '</strong></center></td></tr><tr><td><center><strong>';
-		echo '</strong></center></td>';
-		echo '<td valign="top">';
-		?>
-		
+		<form action="<?php echo getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&amp;atid='.$ath->getID(); ?>" method="post" >        
+		<table>
+		<tr>
+		<td></td><td><center><strong>';
+		<?php echo _('Copy From') ?>
+		<br />
+		<?php echo $fb->getName() ?>
+		</strong></center></td>
+		<td></td>
+		<td><center><strong>
+		<?php echo _('Into trackers and custom fields') ?>
+		</strong></center></td></tr>
+		<tr><td></td>
+		<td valign="top">
 		<input type="hidden" name="copy_opt" value="copy" />
 		<input type="hidden" name="id" value="<?php echo $id; ?>" />
 		<?php
 		echo html_build_multiple_select_box_from_arrays($field_id_arr,$field_arr,'copyid[]',array(),10,false);
 		echo '</td><td><center><strong>';
 		
-//		echo db_error().$sql;
-
 		while($arr =db_fetch_array($res)) {
 				$name_arr[]=$arr['unix_group_name']. '::'. $arr['tracker_name'] . '::'. $arr['field_name'];
 				$id_arr[]=$arr['extra_field_id'];

Modified: trunk/gforge/www/tracker/admin/form-updatecanned.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-updatecanned.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-updatecanned.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -23,7 +23,7 @@
 			<input type="text" name="title" value="<?php echo $acr->getTitle(); ?>" size="50" maxlength="50" />
 			<p>
 			<strong><?php echo _('Message Body') ?>:</strong><br />
-			<textarea name="body" rows="30" cols="65" wrap="hard"><?php echo $acr->getBody(); ?></textarea></p>
+			<textarea name="body" rows="30" cols="65"><?php echo $acr->getBody(); ?></textarea></p>
 			<p>
 			<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
 			</form>

Modified: trunk/gforge/www/tracker/admin/form-updateextrafield.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-updateextrafield.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-updateextrafield.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -19,35 +19,43 @@
 		} else {
 			?>
 			<p>
-			<strong><?php echo _('Type of custom field').': '.$ac->getTypeName(); ?></strong><br />
+			<strong><?php echo _('Type of custom field').': '.$ac->getTypeName(); ?></strong></p>
 			
-			<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&id='.$id.'&atid='.$ath->getID(); ?>" method="post">
+			<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;id='.$id.'&amp;atid='.$ath->getID(); ?>" method="post">
 			<input type="hidden" name="update_box" value="y" />
 			<input type="hidden" name="id" value="<?php echo $ac->getID(); ?>" />
-			<input type="hidden" name="is_required" value="0" />
 			<p>
 			<strong><?php echo _('Custom Field Name') ?>:</strong><br />
 			<input type="text" name="name" value="<?php echo $ac->getName(); ?>" /></p>
 		<p>
-		<?php if ($ac->getType() == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA || $ac->getType() == ARTIFACT_EXTRAFIELDTYPE_TEXT) {?>
-		<?php echo _('Text Fields and Text Areas need to have Size/Maxlength and Rows/Cols defined, respectively.'); ?><br />
-		<?php echo _('Text Field Size/Text Area Rows'); ?> <input type="text" name="attribute1" value="<?php echo $ac->getAttribute1(); ?>" size="2" maxlength="2"><br />
-		<?php echo _('Text Field Maxlength/Text Area Columns'); ?> <input type="text" name="attribute2" value="<?php echo $ac->getAttribute2(); ?>" size="2" maxlength="2">
-		<br />
+		<?php if ($ac->getType() == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) { ?>
+		<b><?php echo _('Text Area Rows'); ?></b><br />
+		<input type="text" name="attribute1" value="<?php echo $ac->getAttribute1(); ?>" size="2" maxlength="2" />
+		</p><p>
+		<b><?php echo _('Text Area Columns'); ?></b><br />
+		<input type="text" name="attribute2" value="<?php echo $ac->getAttribute2(); ?>" size="2" maxlength="2" />
+		<?php } elseif ($ac->getType() == ARTIFACT_EXTRAFIELDTYPE_TEXT || $ac->getType() == ARTIFACT_EXTRAFIELDTYPE_RELATION) {?>
+		<b><?php echo _('Text Field Size'); ?></b><br />
+		<input type="text" name="attribute1" value="<?php echo $ac->getAttribute1(); ?>" size="2" maxlength="2" />
+		</p><p>
+		<b><?php echo _('Text Field Maxlength'); ?></b><br />
+		<input type="text" name="attribute2" value="<?php echo $ac->getAttribute2(); ?>" size="2" maxlength="2" />
 		<?php } else { ?>
 			<input type="hidden" name="attribute1" value="0" />
 			<input type="hidden" name="attribute2" value="0" />
 		<?php } ?>
+			</p>
 			<p>
 			<strong><?php echo _('Field alias') ?>:</strong><br />
-			<input type="text" name="alias" value="<?php echo $ac->getAlias(); ?>" /></p>
+			<input type="text" name="alias" value="<?php echo $ac->getAlias(); ?>" />
 			</p>
+			<p><input type="checkbox" name="is_required" <?php if ($ac->isRequired()) echo "checked=\"checked\""; ?> /><?php echo _('Field is mandatory')?></p>
 			<p>
 			<span class="warning"><?php echo _('It is not recommended that you change the custom field name because other things are dependent upon it. When you change the custom field name, all related items will be changed to the new name') ?>
 				</span></p>
 			<p>
 			<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
-			</form></p>
+			</form>
 			<?php
 		}
 

Modified: trunk/gforge/www/tracker/admin/form-updatetracker.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-updatetracker.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/form-updatetracker.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -42,10 +42,10 @@
 		<input type="text" name="status_timeout"  value="<?php echo($ath->getStatusTimeout() / 86400); ?>" /></p>
 		<p>
 		<strong><?php echo _('Free form text for the "submit new item" page') ?>:</strong><br />
-		<textarea name="submit_instructions" rows="10" cols="55" wrap="hard"><?php echo $ath->getSubmitInstructions(); ?></textarea></p>
+		<textarea name="submit_instructions" rows="10" cols="55"><?php echo $ath->getSubmitInstructions(); ?></textarea></p>
 		<p>
 		<strong><?php echo _('Free form text for the "browse items" page') ?>:</strong><br />
-		<textarea name="browse_instructions" rows="10" cols="55" wrap="hard"><?php echo $ath->getBrowseInstructions(); ?></textarea></p>
+		<textarea name="browse_instructions" rows="10" cols="55"><?php echo $ath->getBrowseInstructions(); ?></textarea></p>
 		<p>
 		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
 		</form>

Added: trunk/gforge/www/tracker/admin/form-workflow.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-workflow.php	                        (rev 0)
+++ trunk/gforge/www/tracker/admin/form-workflow.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,112 @@
+<?php
+
+require_once('common/tracker/ArtifactWorkflow.class.php');
+
+//
+//	FORM TO UPDATE ARTIFACT TYPES
+//
+		$has_error = false;
+		$error_msg = '';
+		$efarr =& $ath->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_STATUS);
+    	if (count($efarr) === 0) {
+    		$has_error = true;
+    		$error_msg .= _('To create a workflow, you need first to create a custom field of type \'Status\'.');
+    	} elseif (count($efarr) !== 1) {
+			// Internal error.
+			$has_error = true;
+    		$error_msg .= 'Internal error: Illegal number of status fields (WKFL01).';
+    	}
+    	
+		$ath->adminHeader(array ('title'=> _('Configure workflow'),'pagename'=>'tracker_admin_customize_liste','titlevals'=>array($ath->getName())));
+
+		/*
+			List of possible user built Selection Boxes for an ArtifactType
+		*/
+    	if (!$has_error) {
+    		
+	    	$keys=array_keys($efarr);
+	    	$field_id = $keys[0];
+	    	$field_name = $efarr[$field_id]['field_name'];
+
+	    	$atw = new ArtifactWorkflow($ath, $field_id);
+	
+			$elearray = $ath->getExtraFieldElements($field_id);
+			$states = $elearray;
+	    	
+	    	?>
+	    	
+	    	<h2><?php printf(_('Allowed initial values for the %1$s field'), $field_name) ?></h2>
+			<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;atid='.$ath->getID(); ?>" method="post">
+			<input type="hidden" name="field_id" value="<?php echo $field_id ?>" />
+			<input type="hidden" name="workflow" value="1" />
+	    	
+	<?php 
+			$from = _('From').' ';
+			$to = _('To').' ';
+			$init = _('Initial values').' ';
+			
+			$title_arr=array();
+			$title_arr[]=_('From Value');
+			foreach ($elearray as $status) {
+				$title_arr[]=$status['element_name'];		
+			}
+			echo $GLOBALS['HTML']->listTableTop($title_arr, false, ' ');
+			echo "\n";
+			
+			// Special treatement for the initial value (in the Submit form).
+			echo '<tr><th style="text-align:left">'.$init.'</th>'."\n";
+			$next = $atw->getNextNodes('100');
+			foreach ($states as $s) {
+				$name = 'wk[100]['. $s['element_id'].']';
+				$value = in_array($s['element_id'], $next)? ' checked="checked"' : '';
+				$str = '<input type="checkbox" name="'.$name.'"'.$value.' />';
+				$str .= ' '.html_image('spacer.gif', 20, 20, array());
+				echo '<td align="center">'.$str.'</td>'."\n";
+			}
+			echo '</tr>'."\n";
+			echo $GLOBALS['HTML']->listTableBottom();
+
+			echo '<h2>'.sprintf(_('Configuring workflow for the %1$s field'), $field_name).'</h2>';
+			
+			$count=count($title_arr);
+			$totitle_arr = array();
+			for ($i=0; $i<$count; $i++) {
+				$totitle_arr[] = $title_arr[$i]? $to.$title_arr[$i] : '';
+			}
+			echo $GLOBALS['HTML']->listTableTop($totitle_arr, false, ' ');
+			
+			foreach ($elearray as $status) {
+				echo '<tr><th style="text-align:left">'.$from.$status['element_name'].'</th>'."\n";
+				$next = $atw->getNextNodes($status['element_id']);
+				foreach ($states as $s) {
+					if ($status['element_id'] !== $s['element_id']) {
+						$name = 'wk['.$status['element_id'].']['. $s['element_id'].']';
+						$value = in_array($s['element_id'], $next)? ' checked="checked"' : '';
+						$str = '<input type="checkbox" name="'.$name.'"'.$value.' />';
+						if ($value) {
+							$url = getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;atid='.$ath->getID().'&amp;workflow_roles=1&amp;from='.$status['element_id'].'&amp;next='.$s['element_id'];
+							$str .= ' <a href="'.$url.'" title="Edit roles">'.html_image('ic/acl_roles20.png', 20, 20, array('alt'=>'Edit Roles')).'</a>';
+						} else {
+							$str .= ' '.html_image('spacer.gif', 20, 20, array());
+						}
+					} else {
+						$str = '<input type="checkbox" checked="checked" disabled="disabled" />';
+						$str .= ' '.html_image('spacer.gif', 20, 20, array());
+					}
+					echo '<td align="center">'.$str.'</td>'."\n";
+				}
+				echo '</tr>'."\n";
+			}
+			echo $GLOBALS['HTML']->listTableBottom();
+
+			?>
+			<div class="tips">Tip: Click on <?php echo html_image('ic/acl_roles20.png', 20, 20, array('alt'=> _('Edit Roles'))) ?> to configure allowed roles for a transition (all by default).</div>	
+			<p>
+			<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
+			</form>
+			<?php
+		}
+
+		$ath->footer(array());
+
+?>

Added: trunk/gforge/www/tracker/admin/form-workflow_roles.php
===================================================================
--- trunk/gforge/www/tracker/admin/form-workflow_roles.php	                        (rev 0)
+++ trunk/gforge/www/tracker/admin/form-workflow_roles.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -0,0 +1,61 @@
+<?php
+
+require_once('common/tracker/ArtifactWorkflow.class.php');
+
+		$from = getIntFromRequest('from');
+		$next = getIntFromRequest('next');
+		
+		//
+//	FORM TO UPDATE ARTIFACT TYPES
+//
+		$ath->adminHeader(array ('title'=> _('Configure allowed roles'),'pagename'=>'tracker_admin_customize_liste','titlevals'=>array($ath->getName())));
+
+		/*
+			List of possible user built Selection Boxes for an ArtifactType
+		*/
+		$efarr =& $ath->getExtraFields(ARTIFACT_EXTRAFIELDTYPE_STATUS);
+    	if (count($efarr) === 0) {
+    		// TODO: Normal status is not implemented right now.
+      		return false;
+    	} elseif (count($efarr) !== 1) {
+			// Internal error.
+			return false;
+    	}
+
+    	$keys=array_keys($efarr);
+    	$field_id = $keys[0];
+    	
+    	$atw = new ArtifactWorkflow($ath, $field_id);
+		$roles = $atw->getAllowedRoles($from, $next);
+		
+		$elearray = $ath->getExtraFieldElements($field_id);
+		foreach ($elearray as $e) {
+			$name[ $e['element_id'] ] = $e['element_name'];
+		}
+
+?>
+    	<h2><?php printf(_('Configuring allowed roles for the transitions from %1$s to %2$s'), $name[$from], $name[$next]) ?></h2>
+ 		<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id.'&amp;atid='.$ath->getID(); ?>" method="post">
+		<input type="hidden" name="field_id" value="<?php echo $field_id ?>" />
+		<input type="hidden" name="workflow_roles" value="1" />
+		<input type="hidden" name="from" value="<?php echo $from ?>" />
+		<input type="hidden" name="next" value="<?php echo $next ?>" />
+    	
+<?php 
+		$res=db_query("SELECT role_id,role_name 
+			FROM role WHERE group_id='$group_id' ORDER BY role_name");
+		while($arr = db_fetch_array($res)) {
+			$value = in_array($arr['role_id'], $roles)? ' checked="checked"' : '';
+			$str = '<input type="checkbox" name="role['.$arr['role_id'].']"'.$value.' />';
+			$str .= ' '.$arr['role_name'];
+			echo $str."<br />\n";
+		}
+?>		
+		<p>
+		<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
+		</form>
+		<?php
+
+		$ath->footer(array());
+
+?>

Modified: trunk/gforge/www/tracker/admin/ind.php
===================================================================
--- trunk/gforge/www/tracker/admin/ind.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/ind.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -40,9 +40,11 @@
 			$res=new ArtifactTypeHtml($group);
 			if (!$res->create($name,$description,$is_public,$allow_anon,$email_all,$email_address,
 				$due_period,$use_resolution,$submit_instructions,$browse_instructions)) {
-				$feedback .= $res->getErrorMessage();
+				exit_error('Error',$res->getErrorMessage());
 			} else {
-				header ("Location: ".util_make_url ("/tracker/admin/?group_id=$group_id&atid=".$res->getID()."&update_users=1"));
+				$feedback .= _('Tracker created successfully');
+				$feedback .= '<br />';
+				$feedback .= _('Please configure also the roles (by default, it\'s \'No Access\')');
 			}
 
 		}
@@ -117,18 +119,17 @@
 	} else {
 
 	?><?php echo _('<h3>Create a new tracker</h3><p>You can use this system to track virtually any kind of data, with each tracker having separate user, group, category, and permission lists. You can also easily move items between trackers when needed.</p><p>Trackers are referred to as "Artifact Types" and individual pieces of data are "Artifacts". "Bugs" might be an Artifact Type, whiles a bug report would be an Artifact. You can create as many Artifact Types as you want, but remember you need to set up categories, groups, and permission for each type, which can get time-consuming') ?>
-	<p>
 	<form action="<?php echo getStringFromServer('PHP_SELF').'?group_id='.$group_id; ?>" method="post">
 	<input type="hidden" name="add_at" value="y" />
 	<p>
 	<?php echo _('<strong> Name:</strong> (examples: meeting minutes, test results, RFP Docs)') ?><br />
-	<input type="text" name="name" value=""></p>
+	<input type="text" name="name" value="" /></p>
 	<p>
 	<strong><?php echo _('Description') ?>:</strong><br />
 	<input type="text" name="description" value="" size="50" /></p>
 	<p>
 	<input type="checkbox" name="is_public" value="1" /> <strong><?php echo _('Publicly Available') ?></strong><br />
-	<input type="checkbox" name="allow_anon" value="1" /> <strong><?php echo _('Allow non-logged-in postings') ?></strong><br />
+	<input type="checkbox" name="allow_anon" value="1" /> <strong><?php echo _('Allow non-logged-in postings') ?></strong></p>
 	<p>
 	<strong><?php echo _('Send email on new submission to address') ?>:</strong><br />
 	<input type="text" name="email_address" value="" /></p>
@@ -142,13 +143,13 @@
 	<input type="text" name="status_timeout" value="14" /></p>
 	<p>
 	<strong><?php echo _('Free form text for the "submit new item" page') ?>:</strong><br />
-	<textarea name="submit_instructions" rows="10" cols="55" wrap="hard"></textarea></p>
+	<textarea name="submit_instructions" rows="10" cols="55"></textarea></p>
 	<p>
 	<strong><?php echo _('Free form text for the "browse items" page') ?>:</strong><br />
-	<textarea name="browse_instructions" rows="10" cols="55" wrap="hard"></textarea></p>
+	<textarea name="browse_instructions" rows="10" cols="55"></textarea></p>
 	<p>
 	<input type="submit" name="post_changes" value="<?php echo _('Submit') ?>" /></p>
-	</form></p>
+	</form>
 	<?php
 	}
 

Modified: trunk/gforge/www/tracker/admin/index.php
===================================================================
--- trunk/gforge/www/tracker/admin/index.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/index.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -55,7 +55,10 @@
 	}
 
 	$next = '';
-	if (getStringFromRequest('post_changes')) {
+	if (getStringFromRequest('post_changes') ||
+		getStringFromRequest('updownorder_opt') ||
+		getStringFromRequest('post_changes_order') ||
+		getStringFromRequest('post_changes_alphaorder')) {
 		include $gfwww.'tracker/admin/updates.php';
 
 	} elseif (getStringFromRequest('deletetemplate')) {
@@ -65,16 +68,17 @@
 		$feedback .= 'Renderer Deleted';
 		$next = 'add_extrafield';
 	}
-	
-	//
-	//	FORMS TO ADD/UPDATE DATABASE
-	//
+
+//
+//		FORMS TO ADD/UPDATE DATABASE
+//
 	if ($next) {
 		$action = $next;
 	} else {
-		$actions = array('add_extrafield', 'add_opt', 'copy_opt', 'add_canned','clone_tracker',
-			'uploadtemplate', 'downloadtemplate', 'update_canned', 'update_box',
-			'update_opt', 'delete', 'deleteextrafield','update_type');
+		$actions = array('add_extrafield', 'customize_list', 'workflow', 'workflow_roles', 'add_opt',
+			'updownorder_opt', 'post_changes_order', 'post_changes_alphaorder', 'copy_opt', 'add_canned',
+			'clone_tracker', 'uploadtemplate', 'downloadtemplate', 'downloadcurrenttemplate', 
+			'update_canned', 'update_box', 'update_opt', 'delete', 'deleteextrafield','update_type');
 		$action = '';
 		foreach ($actions as $a) {
 			if (getStringFromRequest($a)) {
@@ -83,13 +87,28 @@
 			}
 		}
 	}
-		
+
 	if ($action == 'add_extrafield') {  
 
 		include $gfwww.'tracker/admin/form-addextrafield.php';
 
-	} elseif ($action == 'add_opt') {
+	} elseif ($action == 'customize_list') {
 
+		include $gfwww.'tracker/admin/form-customizelist.php';
+
+	} elseif ($action == 'workflow') {
+
+		include $gfwww.'tracker/admin/form-workflow.php';
+
+	} elseif ($action == 'workflow_roles') {
+
+		include $gfwww.'tracker/admin/form-workflow_roles.php';
+
+	} elseif ($action == 'add_opt' ||
+			  $action == 'updownorder_opt' ||
+			  $action == 'post_changes_order' ||
+			  $action == 'post_changes_alphaorder') {
+
 		include $gfwww.'tracker/admin/form-addextrafieldoption.php';
 
 	} elseif ($action == 'copy_opt') {
@@ -112,6 +131,10 @@
 
 		echo $ath->getRenderHTML();
 
+	} elseif ($action == 'downloadcurrenttemplate') {
+
+		echo $ath->getRenderHTML('','DETAIL');
+
 	} elseif ($action == 'update_canned') {
 
 		include $gfwww.'tracker/admin/form-updatecanned.php';

Modified: trunk/gforge/www/tracker/admin/updates.php
===================================================================
--- trunk/gforge/www/tracker/admin/updates.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/admin/updates.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -13,7 +13,7 @@
 			$ab = new ArtifactExtraField($ath);
 		
 			if (!$ab || !is_object($ab)) {
-				$feedback .= 'Unable to create ArtifactExtraField Object';
+				$feedback .= _('Unable to create ArtifactExtraField Object');
 //			} elseif ($ab->isError())
 //				$feedback .= $ab->getErrorMessage();			
 			} else {
@@ -32,7 +32,7 @@
 			$ab = new ArtifactExtraField($ath,$id);
 		
 			if (!$ab || !is_object($ab)) {
-				$feedback .= 'Unable to create ArtifactExtraField Object';
+				$feedback .= _('Unable to create ArtifactExtraField Object');
 			} elseif ($ab->isError()) {
 				$feedback .= $ab->getErrorMessage();			
 			} else {
@@ -41,8 +41,17 @@
 				if (!$ab->delete($sure,$really_sure)) {
 					$feedback .= $ab->getErrorMessage();
 				} else {
+					$browse_list = $ath->getBrowseList();
+					$arr = explode(',', $browse_list);
+					$idx = array_search($id, $arr);
+					if($idx !== False) {
+						array_splice($arr, $idx, 1);
+					}
+					$ath->setBrowseList(join(',', $arr));
 					$feedback .= _('Custom Field Deleted');
-					$next = 'add_extrafield';
+					$deleteextrafield=false;
+					header ("Location: /tracker/admin/?group_id=${group_id}&atid=$atid&add_extrafield=1&feedback=".urlencode($feedback));
+					exit;
 				}
 			}
 
@@ -53,7 +62,7 @@
 			$boxid = getStringFromRequest('boxid');
 			$ab = new ArtifactExtraField($ath,$boxid);
 			if (!$ab || !is_object($ab)) {
-				$feedback .= 'Unable to create ArtifactExtraField Object';
+				$feedback .= _('Unable to create ArtifactExtraField Object');
 			} elseif ($ab->isError()) {
 				$feedback .= $ab->getErrorMessage();			
 			} else {
@@ -83,7 +92,7 @@
 
 			$acr = new ArtifactCanned($ath);
 			if (!$acr || !is_object($acr)) {
-				$feedback .= 'Unable to create ArtifactCanned Object';
+				$feedback .= _('Unable to create ArtifactCanned Object');
 //			} elseif ($acr->isError()) {
 //				$feedback .= $acr->getErrorMessage();			
 			} else { 
@@ -105,7 +114,7 @@
 
 			$acr = new ArtifactCanned($ath,$id);
 			if (!$acr || !is_object($acr)) {
-				$feedback .= 'Unable to create ArtifactCanned Object';
+				$feedback .= _('Unable to create ArtifactCanned Object');
 			} elseif ($acr->isError()) {
 				$feedback .= $acr->getErrorMessage();
 			} else {
@@ -135,18 +144,18 @@
 				$typeid = db_result($result,0,'group_artifact_id');
 				$dest_tracker =& artifactType_get_object($typeid);
 				if (!$dest_tracker || !is_object($dest_tracker)) {
-					exit_error('Error','ArtifactType could not be created');
+					exit_error('Error', _('ArtifactType could not be created'));
 				} elseif ($dest_tracker->isError()) {
 					exit_error(_('Error'),$dest_tracker->getErrorMessage());
 				}
 				//
 				//  Copy elements into a field (box) for each tracker selected 
 				//
-				$feedback .= 'Copy into Tracker: ';
+				$feedback .= _('Copy into Tracker: ');
 				$feedback .= $dest_tracker->getName();
 				$aef =new ArtifactExtraField($dest_tracker,$selectid);
 				if (!$aef || !is_object($aef)) {
-					$feedback .= 'Unable to create ArtifactExtraField Object';
+					$feedback .= _('Unable to create ArtifactExtraField Object');
 				} elseif ($aef->isError()) {
 					$feedback .= $aefe->getErrorMessage();
 				} else {
@@ -189,7 +198,7 @@
 
 			$ac = new ArtifactExtraField($ath,$id);
 			if (!$ac || !is_object($ac)) {
-				$feedback .= 'Unable to create ArtifactExtraField Object';
+				$feedback .= _('Unable to create ArtifactExtraField Object');
 			} elseif ($ac->isError()) {
 				$feedback .= $ac->getErrorMessage();
 			} else {
@@ -212,13 +221,13 @@
 
 			$ac = new ArtifactExtraField($ath,$boxid);
 			if (!$ac || !is_object($ac)) {
-				$feedback .= 'Unable to create ArtifactExtraField Object';
+				$feedback .= _('Unable to create ArtifactExtraField Object');
 			} elseif ($ac->isError()) {
 				$feedback .= $ac->getErrorMessage();
 			} else {
 				$ao = new ArtifactExtraFieldElement($ac,$id);
 				if (!$ao || !is_object($ao)) {
-					$feedback .= 'Unable to create ArtifactExtraFieldElement Object';
+					$feedback .= _('Unable to create ArtifactExtraFieldElement Object');
 				} elseif ($ao->isError()) {
 					$feedback .= $ao->getErrorMessage();
 				} else {
@@ -246,7 +255,7 @@
 			if (!$ath->cloneFieldsFrom($clone_id)) {
 				exit_error('Error','Error cloning fields: '.$ath->getErrorMessage());
 			} else {
-				$feedback .= 'Successfully Cloned Tracker Fields ';
+				$feedback .= _('Successfully Cloned Tracker Fields ');
 				$next = '*main*';
 			}
 
@@ -273,6 +282,30 @@
 			}
 
 		//
+		//	Update the browse list of a tracker
+		//
+		} elseif (getStringFromRequest('customize_list')) {
+			$browse_fields = getArrayFromRequest('browse_fields');
+			foreach ($browse_fields as $name => $pos) {
+				if ($pos)
+					$list_fields[$pos][] = $name;
+			}
+			ksort($list_fields);
+			$browse_fields = array();
+			foreach ($list_fields as $pos => $list_name) {
+				sort($list_name);
+				foreach ($list_name as $name)
+					$browse_fields[] = $name;
+			}
+			$browse_fields = join(',', $browse_fields);
+			if (!$ath->setBrowseList($browse_fields)) {
+				$feedback .= _('Error updating').' : '.$ath->getErrorMessage();
+				$ath->clearError();
+			} else {
+				$feedback .= _('Tracker Updated');
+			}
+
+		//
 		//	Delete a tracker
 		//
 		} elseif (getStringFromRequest('delete')) {
@@ -292,8 +325,8 @@
 		} elseif (getStringFromRequest('uploadtemplate')) {
 
 			$input_file = getUploadedFile('input_file');
-			if (!util_check_fileupload($input_file)) {
-				echo ('Invalid filename');
+			if (!util_check_fileupload($input_file['tmp_name'])) {
+				echo ('Invalid filename :'.$input_file['tmp_name']);
 				exit;
 			}
 			$size = $input_file['size'];
@@ -301,8 +334,102 @@
 
 			db_query("UPDATE artifact_group_list SET custom_renderer='$input_data' WHERE group_artifact_id='".$ath->getID()."'");
 			echo db_error();
-			$feedback .= 'Renderer Uploaded';
+			$feedback .= _('Renderer Uploaded');
+		//
+		//	Up or down elements
+		//
+		} elseif (getStringFromRequest('updownorder_opt')) {
+			$boxid = getStringFromRequest('boxid');
+			$id = getIntFromRequest('id');
+			$new_pos = getStringFromRequest('new_pos');
+			$ac = new ArtifactExtraField($ath,$boxid);
+			if (!$ac || !is_object($ac)) {
+				$feedback .= _('Unable to create ArtifactExtraField Object');
+			} elseif ($ac->isError()) {
+				$feedback .= $ac->getErrorMessage();
+			} else {
+				if (!$ac->reorderValues($id, $new_pos)) {
+					$feedback .= _('Error updating a custom field').' : '.$ac->getErrorMessage();
+					$ac->clearError();
+				} else {
+					$feedback .= _('Tracker Updated');
+				}
+			}
 
+		//
+		//  Change order of elements
+		//
+		} elseif (getStringFromRequest('post_changes_order')) {
+			$boxid = getStringFromRequest('boxid');
+			$order = getArrayFromRequest('order');
+			$ac = new ArtifactExtraField($ath,$boxid);
+			if (!$ac || !is_object($ac)) {
+				$feedback .= 'Unable to create ArtifactExtraField Object';
+			} elseif ($ac->isError()) {
+				$feedback .= $ac->getErrorMessage();
+			} else {
+				$updated_flag = 0;
+				foreach ($order as $id => $new_pos) {
+					if ($new_pos == '') continue;
+					if (!$ac->reorderValues($id, $new_pos)) {
+						$feedback .= _('Error updating a custom field').' : '.$ac->getErrorMessage();
+						$ac->clearError();
+						continue;
+					}
+					else {
+						$updated_flag = 1;
+					}
+				}
+				if ($updated_flag)
+					$feedback .= _('Tracker Updated');
+			}
+		} elseif (getStringFromRequest('post_changes_alphaorder')) {
+			$boxid = getStringFromRequest('boxid');
+			$ac = new ArtifactExtraField($ath,$boxid);
+			if (!$ac || !is_object($ac)) {
+				$feedback .= 'Unable to create ArtifactExtraField Object';
+			} elseif ($ac->isError()) {
+				$feedback .= $ac->getErrorMessage();
+			} else {
+				if (!$ac->alphaorderValues($id)) {
+					$feedback .= _('Error updating a custom field').' : '.$ac->getErrorMessage();
+					$ac->clearError();
+				} else {
+					$feedback .= _('Tracker Updated');
+				}
+			}
+		//
+		// Configure workflow
+		//
+		} elseif (getStringFromRequest('workflow')) {
+			require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
+			$field_id = getIntFromRequest('field_id');
+			$wk = getArrayFromRequest('wk');
+    		$atw = new ArtifactWorkflow($ath, $field_id);
+
+    		if (!isset($wk[100])) {
+    			$feedback .= _('ERROR: Initial values not saved, no initial state given.').'<br />';
+    		} else {
+	    		// Save values for the submit form (from=100).
+	    		$atw->saveNextNodes('100', array_keys($wk[100]));
+    			$feedback .= _('Initial values saved.').'<br />';
+    		}
+    			
+	    	$elearray = $ath->getExtraFieldElements($field_id);
+			foreach ($elearray as $e) {
+				$from = $e['element_id'];
+				$next = isset($wk[$from]) ? array_keys($wk[$from]) : array();
+				$atw->saveNextNodes($from, array_keys($wk[$from]));
+	 		}
+			$feedback .= _('Workflow saved');
+		} elseif (getStringFromRequest('workflow_roles')) {
+			require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
+			$field_id = getIntFromRequest('field_id');
+			$from = getIntFromRequest('from');
+			$next = getIntFromRequest('next');
+			$role = array_keys(getArrayFromRequest('role'));
+			$atw = new ArtifactWorkflow($ath, $field_id);
+    		$atw->saveAllowedRoles($from, $next, $role);
+			$feedback .= _('Workflow saved');
 		}
-
-?>
+		?>

Modified: trunk/gforge/www/tracker/browse.php
===================================================================
--- trunk/gforge/www/tracker/browse.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/browse.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -16,6 +16,8 @@
 	exit_permission_denied();
 }
 
+$query_id = getIntFromRequest('query_id');
+
 //
 //	The browse page can be powered by a pre-saved query
 //	or by select boxes chosen by the user
@@ -26,8 +28,6 @@
 //	If the query_id = -1, unset the pref and use regular browse boxes
 //
 if (session_loggedin()) {
-	$query_id = getIntFromRequest('query_id');
-
 	if($query_id) {
 		if ($query_id == '-1') {
 			$u =& session_get_user();
@@ -43,6 +43,20 @@
 		$u =& session_get_user();
 		$query_id=$u->getPreference('art_query'.$ath->getID(),'');
 	}
+} elseif ($query_id) {
+	// If user is not logged, then use a cookie to store the current query.
+	if (isset($_COOKIE["GFTrackerQuery"])) {
+		$gf_tracker = unserialize($_COOKIE["GFTrackerQuery"]);
+	} else {
+		$gf_tracker = array();
+	}
+	$gf_tracker[$ath->getID()] = $query_id;
+	// Send the query_id as a cookie to save it.
+	setcookie("GFTrackerQuery", serialize($gf_tracker));
+	$_COOKIE["GFTrackerQuery"] = serialize($gf_tracker);
+} elseif (isset($_COOKIE["GFTrackerQuery"])) {
+	$gf_tracker = unserialize($_COOKIE["GFTrackerQuery"]);
+	$query_id = (int)$gf_tracker[$ath->getID()];
 }
 
 $af = new ArtifactFactory($ath);
@@ -182,6 +196,12 @@
 $changed_arr[]= 3600 * 24 * 14;// 2 week
 $changed_arr[]= 3600 * 24 * 30;// 1 month
 
+/**
+ *
+ *	Show the free-form text submitted by the project admin
+ */
+echo $ath->renderBrowseInstructions();
+
 //
 //	statuses can be custom in GForge 4.5+
 //
@@ -204,70 +224,109 @@
 	}
 	$status_box = $ath->statusBox('_status',$_status,true,_('Any'));
 }
+echo '<script type="text/javascript" src="/tabber/tabber.js"></script>'."\n";
 echo '
-<table width="100%" border="0">';
+<div id="tabber" class="tabber">
+	<div class="tabbertab" title="'._('Advanced queries').'">';
 
-echo '
-	<tr>';
-/*
-	Logged in users get the option of seeing a power-browse box
-*/
 if (session_loggedin()) {
-	echo '<td rowspan="2">';
-	echo '<form action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&atid='.$ath->getID().'" method="post">';
-	echo '<input type="hidden" name="power_query" value="1">';
-	$res=db_query("SELECT artifact_query_id,query_name 
-	FROM artifact_query WHERE user_id='".user_getid()."' AND group_artifact_id='".$ath->getID()."'");
+	$filter = "AND (user_id='".user_getid()."' OR query_type>0)";
+} else {
+	$filter = "AND query_type>0";
+}
 
-	if (db_numrows($res)>0) {
-	echo 
-		html_build_select_box($res,'query_id',$af->getDefaultQuery(),false).'<br />
-		<input type="submit" name="run" value="'._('Power Query').'"></input>
-		<strong><a href="javascript:admin_window(\''.util_make_url ('/tracker/?func=query&group_id='.$group_id.'&atid='.$ath->getID()).'\')">'.
-		_('Build Query').'</a></strong>';
-	} else {
-		echo '<strong>
-		<a href="javascript:admin_window(\''.util_make_url ('/tracker/?func=query&group_id='.$group_id.'&atid='.$ath->getID()).'\')">'._('Build Query').'</a></strong>';
+$sql="SELECT artifact_query_id,query_name, CASE WHEN query_type>0 THEN 1 ELSE 0 END as type 
+	FROM artifact_query 
+	WHERE group_artifact_id='".$ath->getID()."' $filter
+	ORDER BY type ASC, query_name ASC";
+$res = db_query($sql);
+
+if (db_numrows($res)>0) {
+	echo '<form action="'. getStringFromServer('PHP_SELF') .'" method="get">';
+	echo '<input type="hidden" name="group_id" value="'.$group_id.'" />';
+	echo '<input type="hidden" name="atid" value="'.$ath->getID().'" />';
+	echo '<input type="hidden" name="power_query" value="1" />';
+	echo '	<table width="100%" cellspacing="0">
+	<tr>
+	<td>
+	';
+	$optgroup['key'] = 'type';
+	$optgroup['values'][0] = 'Private queries';
+	$optgroup['values'][1] = 'Project queries';
+	echo '<span style="font-size:smaller">';
+	echo '<select name="query_id">';
+	echo '<option value="100">Select One</option>';
+	$current = '';
+	$selected = $af->getDefaultQuery();
+	while ($row = db_fetch_array($res)) {
+		if ($current != $row['type']) {
+			if ($current !== '') 
+				echo '</optgroup>';
+			$label = $row['type'] ? 'Project' : 'Private';
+			echo '<optgroup label="'.$label.'">';
+			$current = $row['type'];
+		}
+		echo '<option value="'.$row['artifact_query_id'].'"';
+		if ($row['artifact_query_id'] == $selected)
+			echo ' selected="selected"';
+		echo '>'. $row['query_name'] .'</option>'."\n";
 	}
-	echo '
-		</form>
-		</td>';
+	if ($current !== '') 
+		echo '</optgroup>';
+	echo '</select>';
+	echo '</span>
+	<input type="submit" name="run" value="'._('Power Query').'" />
+	&nbsp;&nbsp;<a href="/tracker/?atid='. $ath->getID().'&amp;group_id='.$group_id.'&amp;func=query">'.
+	_('Build Query').'</a>
+	</td></tr></table>
+	</form>';
+} else {
+	echo '<strong>
+	<a href="/tracker/?atid='. $ath->getID().'&amp;group_id='.$group_id.'&amp;func=query">'._('Build Query').'</a></strong>';
 }
 echo '
-	<form action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&atid='.$ath->getID().'" method="post">
+	</div>
+	<div class="tabbertab'.($af->query_type == 'custom' ? ' tabbertabdefault' : '').'" title="'._('Simple Filtering and Sorting').'">
+	<form action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&amp;atid='.$ath->getID().'" method="post">
+	<input type="hidden" name="query_id" value="-1" />
 	<input type="hidden" name="set" value="custom" />
-	<td>'._('Assignee').'<br />'. $tech_box .'</td>'.
-	'<td>'._('Status').'<br />'. $status_box .'</td>';
-	echo '
-</tr>
-
-<input type="hidden" name="query_id" value="-1">';
-
-	echo '
+	<table width="100%" cellspacing="0">
 	<tr>
-		<td align="right">'._('Order by').' <a href="javascript:help_window(\''.util_make_url ('/help/tracker.php?helpname=sort_by').'\')"><strong>(?)</strong></a></span></td>'.
-		'<td>'. 
-		html_build_select_box_from_arrays($order_arr,$order_name_arr,'_sort_col',$_sort_col,false) .
-		html_build_select_box_from_arrays($sort_arr,$sort_name_arr,'_sort_ord',$_sort_ord,false) .
-		'<input type="submit" name="submit" value="'._('Quick Browse').'" /></span></td>
-	</tr>';
+	<td>
+	'._('Assignee').':&nbsp;'. $tech_box .'
+	</td>
+	<td align="center">
+	'._('State').':&nbsp;'. $status_box .'
+	</td>
+	<td align="right">';
 
+echo _('Order by').
+	':&nbsp;<a href="javascript:help_window(\'/help/tracker.php?helpname=sort_by\')">' .
+	'<strong>(?)</strong></a>'.
+	html_build_select_box_from_arrays($order_arr,$order_name_arr,'_sort_col',$_sort_col,false) .
+	html_build_select_box_from_arrays($sort_arr,$sort_name_arr,'_sort_ord',$_sort_ord,false) .
+	'<input type="submit" name="submit" value="'._('Quick Browse').'" />';
 
 echo '
+	</td>
+	</tr>
+	</table>
 	</form>
-</table>';
-/**
- *
- *	Show the free-form text submitted by the project admin
- */
-echo $ath->getBrowseInstructions();
+	</div>';
+if ($af->query_type == 'default') {
+	echo '<div class="tabbertab tabbertabdefault" title="'._('Default').'">';
+	echo '<strong>'._('Viewing only opened records by default, use \'Advanced queries\' or \'Simple Filtering and Sorting\' to change.').'</strong>';
+	echo '</div>';
+}
+echo '
+</div>';
 
 if ($art_arr && count($art_arr) > 0) {
 
 	if ($set=='custom') {
-		$set .= '&_assigned_to='.$_assigned_to.'&_status='.$_status.'&_sort_col='.$_sort_col.'&_sort_ord='.$_sort_ord;
+		$set .= '&amp;_assigned_to='.$_assigned_to.'&amp;_status='.$_status.'&amp;_sort_col='.$_sort_col.'&amp;_sort_ord='.$_sort_ord;
 		if (array_key_exists($ath->getCustomStatusField(),$_extra_fields)) {
-			$set .= '&extra_fields['.$ath->getCustomStatusField().']='.$_extra_fields[$ath->getCustomStatusField()];
+			$set .= '&amp;extra_fields['.$ath->getCustomStatusField().']='.$_extra_fields[$ath->getCustomStatusField()];
 		}
 	}
 
@@ -276,34 +335,39 @@
 
 	if ($IS_ADMIN) {
 		echo '
-		<form name="artifactList" action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&atid='.$ath->getID().'" METHOD="POST">
-		<input type="hidden" name="form_key" value="'.form_generate_key().'">
-		<input type="hidden" name="func" value="massupdate">';
+		<form name="artifactList" action="'. getStringFromServer('PHP_SELF') .'?group_id='.$group_id.'&amp;atid='.$ath->getID().'" method="post">
+		<input type="hidden" name="form_key" value="'.form_generate_key().'" />
+		<input type="hidden" name="func" value="massupdate" />';
 	}
 
-	$display_col=array('summary'=>1,
-		'open_date'=>1,
-		'status'=>0,
-		'priority'=>1,
-		'assigned_to'=>1,
-		'submitted_by'=>1);
-
+	$browse_fields = explode(',', "id,".$ath->getBrowseList());
 	$title_arr=array();
-	$title_arr[]=_('ID');
-	if ($display_col['summary'])
-		$title_arr[]=_('Summary');
-	if ($display_col['open_date'])
-		$title_arr[]=_('Open Date');
-	if ($display_col['status'])
-		$title_arr[]=_('State');
-	if ($display_col['priority'])
-		$title_arr[]=_('Priority');
-	if ($display_col['assigned_to'])
-		$title_arr[]=_('Assigned to');
-	if ($display_col['submitted_by'])
-		$title_arr[]=_('Submitted by');
+	foreach ($browse_fields as $f) {
+		if (intval($f) > 0) {
+    		$title = $ath->getExtraFieldName($f);
+		} else {
+			if ($f == 'id')
+				$title=_('ID');
+			if ($f == 'summary')
+				$title=_('Summary');
+			if ($f == 'details')
+				$title=_('Description');
+			if ($f == 'open_date')
+				$title=_('Open Date');
+			if ($f == 'close_date')
+				$title=_('Close Date');
+			if ($f == 'status_id')
+				$title=_('State');
+			if ($f == 'priority')
+				$title=_('Priority');
+			if ($f == 'assigned_to')
+				$title=_('Assigned to');
+			if ($f == 'submitted_by')
+				$title=_('Submitted by');
+		}
+		$title_arr[] = $title;
+	}
 
-
 	echo $GLOBALS['HTML']->listTableTop ($title_arr);
 
 	$then=(time()-$ath->getDuePeriod());
@@ -316,44 +380,67 @@
 	$max = ((count($art_arr) > ($start + 25)) ? ($start+25) : count($art_arr) );
 //echo "max: $max";
 	for ($i=$start; $i<$max; $i++) {
+ 		$extra_data = $art_arr[$i]->getExtraFieldDataText();
 		echo '
-		<tr '. $HTML->boxGetAltRowStyle($i) . '>'.
-		'<td>'.
-		($IS_ADMIN?'<input type="CHECKBOX" name="artifact_id_list[]" value="'.
-			$art_arr[$i]->getID() .'"> ':'').
-			$art_arr[$i]->getID() .
-			'</td>';
-		if ($display_col['summary'])
-		 echo '<td><a href="'.getStringFromServer('PHP_SELF').'?func=detail&amp;aid='.
-			$art_arr[$i]->getID() .
-			'&amp;group_id='. $group_id .'&amp;atid='.
-			$ath->getID().'">'.
-			$art_arr[$i]->getSummary().
-			'</a></td>';
-		if ($display_col['open_date'])
-			echo '<td>'. (($set != 'closed' && $art_arr[$i]->getOpenDate() < $then)?'* ':'&nbsp; ') .
+		<tr '. $HTML->boxGetAltRowStyle($i) . '>';
+ 		foreach ($browse_fields as $f) {
+			if ($f == 'id') {
+				echo '<td nowrap="nowrap">'.
+				($IS_ADMIN?'<input type="checkbox" name="artifact_id_list[]" value="'.
+				$art_arr[$i]->getID() .'" /> ':'').
+				'<a href="'.getStringFromServer('PHP_SELF').'?func=detail&amp;aid='.
+				$art_arr[$i]->getID() .
+				'&amp;group_id='. $group_id .'&amp;atid='.
+				$ath->getID().'">'.$art_arr[$i]->getID() .
+				'</a></td>';
+			} else if ($f == 'summary') {
+		 		echo '<td><a href="'.getStringFromServer('PHP_SELF').'?func=detail&amp;aid='.
+				$art_arr[$i]->getID() .
+				'&amp;group_id='. $group_id .'&amp;atid='.
+				$ath->getID().'">'.
+				$art_arr[$i]->getSummary().
+				'</a></td>';
+			} else if ($f == 'open_date') {
+				echo '<td>'. (($set != 'closed' && $art_arr[$i]->getOpenDate() < $then)?'* ':'&nbsp; ') .
 				date(_('Y-m-d H:i'),$art_arr[$i]->getOpenDate()) .'</td>';
-		if ($display_col['status'])
-			echo '<td>'. $art_arr[$i]->getStatusName() .'</td>';
-		if ($display_col['priority'])
-			echo '<td class="priority'.$art_arr[$i]->getPriority()  .'">'. $art_arr[$i]->getPriority() .'</td>';
-		if ($display_col['assigned_to'])
-			echo '<td>'. $art_arr[$i]->getAssignedRealName() .'</td>';
-		if ($display_col['submitted_by'])
-			echo '<td>'. $art_arr[$i]->getSubmittedRealName() .'</td>';
+			} else if ($f == 'status_id') {
+				echo '<td>'. $art_arr[$i]->getStatusName() .'</td>';
+			} else if ($f == 'priority') {
+				echo '<td class="priority'.$art_arr[$i]->getPriority()  .'">'. $art_arr[$i]->getPriority() .'</td>';
+			} else if ($f == 'assigned_to') {
+				echo '<td>'. $art_arr[$i]->getAssignedRealName() .'</td>';
+			} else if ($f == 'submitted_by') {
+				echo '<td>'. $art_arr[$i]->getSubmittedRealName() .'</td>';
+			} else if ($f == 'close_date') {
+				echo '<td>'. ($art_arr[$i]->getCloseDate() ? 
+				date(_('Y-m-d H:i'),$art_arr[$i]->getCloseDate()) :'&nbsp; ') .'</td>';
+			} else if ($f == 'details') {
+				echo '<td>'. $art_arr[$i]->getDetails() .'</td>';
+			} else if (intval($f) > 0) {
+				// Now display extra-fields (fields are numbers).
+				$value = $extra_data[$f]['value'];
+				if ($extra_data[$f]['type'] == 9) {
+					$value = preg_replace('/\b(\d+)\b/e', "_artifactid2url('\\1')", $value);
+				}
+				echo '<td>' . $value .'</td>';
+			} else {
+				// Display ? for unknown values.
+				echo '<td>?</td>';
+			}
+ 		}
 		echo '</tr>';
 	}
 
 	/*
 		Show extra rows for <-- Prev / Next -->
 	* /
-	//only show this if we�re not using a power query
+	//only show this if we're not using a power query
 	if ($af->max_rows > 0) {
 		if (($offset > 0) || ($rows >= 50)) {
 			echo '
 				<tr><td colspan="2">';
 			if ($offset > 0) {
-				echo '<a href="'.getStringFromServer('PHP_SELF').'?func=browse&amp;group_id='.$group_id.'&amp;atid='.$ath->getID().'&set='.
+				echo '<a href="'.getStringFromServer('PHP_SELF').'?func=browse&amp;group_id='.$group_id.'&amp;atid='.$ath->getID().'&amp;set='.
 				$set.'&offset='.($offset-50).'"><strong><-- '._('Previous 50').'</strong></a>';
 			} else {
 				echo '&nbsp;';
@@ -373,31 +460,33 @@
 	$pages = count($art_arr) / 25;
 	$currentpage = intval($start / 25);
 //echo "Item Count: ".count($arr)."Pages: $pages";
-	$skipped_pages=false;
-	for ($j=0; $j<$pages; $j++) {
-		if ($pages > 20) {
-			if ((($j > 4) && ($j < ($currentpage-5))) || (($j > ($currentpage+5)) && ($j < ($pages-5)))) {
-				if (!$skipped_pages) {
-					$skipped_pages=true;
-					echo "....&nbsp;";
+	if ($pages >= 1) {
+		$skipped_pages=false;
+		for ($j=0; $j<$pages; $j++) {
+			if ($pages > 20) {
+				if ((($j > 4) && ($j < ($currentpage-5))) || (($j > ($currentpage+5)) && ($j < ($pages-5)))) {
+					if (!$skipped_pages) {
+						$skipped_pages=true;
+						echo "....&nbsp;";
+					}
+					continue;
+				} else {
+					$skipped_pages=false;
 				}
-				continue;
+			}
+			if ($j == $currentpage) {
+				echo '<strong>'.($j+1).'</strong>&nbsp;&nbsp;';
 			} else {
-				$skipped_pages=false;
+				echo '<a href="'.getStringFromServer('PHP_SELF')."?func=browse&amp;group_id=".$group_id.'&amp;atid='.$ath->getID().'&amp;set='. $set.'&amp;start='.($j*25).'"><strong>'.($j+1).'</strong></a>&nbsp;&nbsp;';
 			}
 		}
-		if ($j == $currentpage) {
-			echo '<strong>'.($j+1).'</strong>&nbsp;&nbsp;';
-		} else {
-			echo '<a href="'.getStringFromServer('PHP_SELF')."?func=browse&amp;group_id=".$group_id.'&atid='.$ath->getID().'&set='. $set.'&start='.($j*25).'"><strong>'.($j+1).'</strong></a>&nbsp;&nbsp;';
-		}
 	}
 
 	/*
 		Mass Update Code
 	*/
 	if ($IS_ADMIN) {
-		echo '<script language="JavaScript">
+		echo '<script type="text/javascript">
 	<!--
 	function checkAll(val) {
 		al=document.artifactList;
@@ -412,7 +501,7 @@
 	//-->
 	</script>
 
-			<table width="100%" border="0">
+			<table width="100%" border="0" id="admin_mass_update">
 			<tr><td colspan="2">
 
 <a href="javascript:checkAll(1)">'._('Check &nbsp;all').'</a>
@@ -420,7 +509,7 @@
   <a href="javascript:checkAll(0)">'._('Clear &nbsp;all').'</a>
 
 <p>
-<span class="important">'._('<strong>Admin:</strong> If you wish to apply changes to all items selected above, use these controls to change their properties and click once on "Mass Update".').'
+<span class="important">'._('<strong>Admin:</strong> If you wish to apply changes to all items selected above, use these controls to change their properties and click once on "Mass Update".').'</span></p>
 			</td></tr>';
 
 
@@ -463,8 +552,7 @@
 				<a href="javascript:help_window(\'/help/tracker.php?helpname=canned_response\')"><strong>(?)</strong></a>
 				</strong><br />'. $ath->cannedResponseBox ('canned_response') .'</td></tr>
 
-			<tr><td colspan="3" align="MIDDLE"><input type="SUBMIT" name="submit" value="'._('Mass update').'"></td></tr>
-
+			<tr><td colspan="3" align="center"><input type="submit" name="submit" value="'._('Mass update').'" /></td></tr>
 			</table>
 		</form>';
 	}

Modified: trunk/gforge/www/tracker/detail.php
===================================================================
--- trunk/gforge/www/tracker/detail.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/detail.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -24,7 +24,7 @@
 				<?php 
 					if ($ah->isMonitoring()) {
 						$img="xmail16w.png";
-						$key="stop_monitoring";
+						$key="monitorstop";
 					} else {
 						$img="mail16w.png";
 						$key="monitor";
@@ -66,7 +66,7 @@
 		</tr>
 
 	<?php
-		//$ath->renderExtraFields($ah->getExtraFieldData(),true);
+		$ath->renderExtraFields($ah->getExtraFieldData(),true,'none',false,'Any','',false,'DISPLAY');
 	?>
 
 		<tr><td colspan="2"><strong><?php echo _('Summary') ?>:</strong><br /><?php echo $ah->getSummary(); ?></td></tr>
@@ -83,18 +83,19 @@
 	<table border="0" width="80%">
 		<tr><td colspan="2">
 			<?php if ($ath->allowsAnon() || session_loggedin()) { ?>
-			<input type="hidden" name="form_key" value="<?php echo form_generate_key(); ?>">
-			<input type="hidden" name="func" value="postmod">
-			<input type="hidden" name="artifact_id" value="<?php echo $ah->getID(); ?>">
+			<input type="hidden" name="form_key" value="<?php echo form_generate_key(); ?>" />
+			<input type="hidden" name="func" value="postmod" />
+			<input type="hidden" name="MAX_FILE_SIZE" value="10000000" />
+			<input type="hidden" name="artifact_id" value="<?php echo $ah->getID(); ?>" />
 			<p>
 			<strong><?php echo _('Add A Comment') ?>:</strong> 
 			<?php echo notepad_button('document.forms[1].details') ?><br />
-			<textarea name="details" ROWS="10" COLS="60" WRAP="SOFT"></textarea>
+			<textarea name="details" rows="10" cols="60"></textarea>
+			</p>
 			<?php } ?>
 		</td></tr>
 		<tr><td colspan="2">
 		<h3><?php echo _('Followup') ?></h3>
-		<p>
 		<?php
 		echo $ah->showMessages();
 		?>
@@ -143,7 +144,7 @@
 <?php } ?>
 <div class="tabbertab" title="<?php echo _('Attachments'); ?>">
 <table border="0" width="80%">
-	<tr><td colspan=2>
+	<tr><td colspan="2">
 	<?php if (session_loggedin() && ($ah->getSubmittedBy() == user_getid())) { ?>
 		<strong><?php echo _('Attach Files'); ?></strong><br />
 		<input type="file" name="input_file[]" size="30" /><br />
@@ -179,9 +180,8 @@
 	} else {
 		echo _('No Files Currently Attached');
 	}
-	
-
 ?>
+	</td></tr>
 </table>
 </div>
 <div class="tabbertab" title="<?php echo _('Commits'); ?>" >
@@ -197,7 +197,6 @@
 	<tr>
 	<td colspan="2">
 	<h3><?php echo _('Changes') ?>:</h3>
-	<p>
 	<?php
 
 	echo $ah->showHistory();
@@ -207,7 +206,9 @@
 	</tr>
 </table>
 </div>
+<?php $ah->showRelations(); ?>
 </div>
+</form>
 <?php
 
 $ath->footer(array());

Modified: trunk/gforge/www/tracker/downloadcsv.php
===================================================================
--- trunk/gforge/www/tracker/downloadcsv.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/downloadcsv.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -20,10 +20,11 @@
  */
 require_once $gfcommon.'tracker/ArtifactFactory.class.php';
 
-header('Content-type: text/comma-separated-values');
-list($year, $month) = explode('-', date('Y-m'));
-header('Content-disposition: filename="tracker_report-'.$year.'-'.$month.'.csv"');
+$date = date('Y-m-d');
 
+header('Content-type: text/csv');
+header('Content-disposition: filename="tracker_report-'.$date.'.csv"');
+
 if (!$ath->userCanView()) {
 	exit_permission_denied();
 }
@@ -48,42 +49,48 @@
 
 $at_arr =& $af->getArtifacts();
 
-echo 'artifact_id,status_id,status_name,priority,submitter_id,submitter_name,assigned_to_id,assigned_to_name,open_date,close_date,last_modified_date,summary';
+echo 'artifact_id;status_id;status_name;priority;submitter_id;submitter_name;assigned_to_id;assigned_to_name;open_date;close_date;last_modified_date;summary;details';
 
 //
 //	Show the extra fields
 //
-$ef =& $ath->getExtraFields(ARTIFACT_EXTRAFIELD_FILTER_INT);
+$ef =& $ath->getExtraFields();
 $keys=array_keys($ef);
 for ($i=0; $i<count($keys); $i++) {
-	echo ',"'.$ef[$keys[$i]]['field_name'].'"';
+	echo ';"'.$ef[$keys[$i]]['field_name'].'"';
 }
 
-$arrRemove = array("\r\n", "\n", ',');
-
 for ($i=0; $i<count($at_arr); $i++) {
 
-	echo "\n".$at_arr[$i]->getID().','.
-		$at_arr[$i]->getStatusID().',"'.
-		$at_arr[$i]->getStatusName().'",'.
-		$at_arr[$i]->getPriority().','.
-		$at_arr[$i]->getSubmittedBy().',"'.
-		$at_arr[$i]->getSubmittedRealName().'",'.
-		$at_arr[$i]->getAssignedTo().',"'.
-		$at_arr[$i]->getAssignedRealName().'","'.
-		date(_('Y-m-d H:i'),$at_arr[$i]->getOpenDate()).'","'.
-		date(_('Y-m-d H:i'),$at_arr[$i]->getCloseDate()).'","'.
-		date(_('Y-m-d H:i'),$at_arr[$i]->getLastModifiedDate()).'","'.
-		$at_arr[$i]->getSummary().'"';
+	echo "\n".$at_arr[$i]->getID().';'.
+		$at_arr[$i]->getStatusID().';"'.
+		$at_arr[$i]->getStatusName().'";'.
+		$at_arr[$i]->getPriority().';'.
+		$at_arr[$i]->getSubmittedBy().';"'.
+		$at_arr[$i]->getSubmittedRealName().'";'.
+		$at_arr[$i]->getAssignedTo().';"'.
+		$at_arr[$i]->getAssignedRealName().'";"'.
+		date(_('Y-m-d H:i'),$at_arr[$i]->getOpenDate()).'";"'.
+		date(_('Y-m-d H:i'),$at_arr[$i]->getCloseDate()).'";"'.
+		date(_('Y-m-d H:i'),$at_arr[$i]->getLastModifiedDate()).'";"'.
+		fix4csv($at_arr[$i]->getSummary()).'";"'.
+		fix4csv($at_arr[$i]->getDetails()).'"';
 
 	//
 	//	Show the extra fields
 	//
-	$efd =& $at_arr[$i]->getExtraFieldData();
-	for ($j=0; $j<count($keys); $j++) {
-		$v=$efd[$keys[$j]];
-		echo ',"'.$ath->getElementName($v).'"';
-	}
+ 	$efd =& $at_arr[$i]->getExtraFieldDataText();
+ 	foreach ( $efd as $efd_pair ) {
+ 		$value = $efd_pair["value"];
+ 		echo ';"'. fix4csv($value) .'"';
+ 	}
 }
 
+function fix4csv ($value) {
+	$value =& util_unconvert_htmlspecialchars( $value );
+	$value =& str_replace("\r\n", "\n", $value);
+	$value =& str_replace('"', '""', $value);
+	return $value;
+}
+
 ?>

Modified: trunk/gforge/www/tracker/include/ArtifactHtml.class.php
===================================================================
--- trunk/gforge/www/tracker/include/ArtifactHtml.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/include/ArtifactHtml.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -11,6 +11,7 @@
 
 
 require_once $gfcommon.'tracker/Artifact.class.php';
+require_once $gfcommon.'include/utils_crossref.php';
 
 class ArtifactHtml extends Artifact {
 
@@ -30,14 +31,29 @@
 	/**
 	 * show details preformatted (like followups)
 	 */
-	function showDetails() {
+	function showDetails($editable = false) {
 		$result = $this->getDetails();
+		$result = util_gen_cross_ref($result, $this->ArtifactType->Group->getID());
+		//$result = util_line_wrap( $result, 120,"\n");
+		$result = preg_replace('/\r?\n/', '<br />', $result);
 
 		$title_arr = array();
-		$title_arr[] = _('Detailed description');
+		if ($editable === true) {
+			$title_arr[] = '<div style="width:100%;">' .
+				'<div style="float:left">' . _('Detailed description') . '</div>' .
+				'<div style="float:right">' . html_image('ic/forum_edit.gif','37','15',array('title'=>"Click to edit", 'alt'=>"Click to edit", 'onclick'=>"switch2edit(this, 'show', 'edit')", 'border'=>"0")) . '</div>' .
+/*
+ html_image('ic/forum_edit.gif','37','15',array(title=>"Click to edit" alt=>"Click to edit" onclick=>"switch2edit(this, 'show', 'edit')", border=>"0"))
+ */
+			
+				'</div>';
+		}
+		else {
+			$title_arr[] = _('Detailed description');
+		}
 		echo $GLOBALS['HTML']->listTableTop ($title_arr);
 
-		echo '<tr ' . $GLOBALS['HTML']->boxGetAltRowStyle(0) .'><td><pre>'. util_line_wrap ( $result, 120,"\n"). '</pre></td></tr>';
+		echo '<tr ' . $GLOBALS['HTML']->boxGetAltRowStyle(0) .'><td>'. $result. '</td></tr>';
 
 		echo $GLOBALS['HTML']->listTableBottom();
 	}
@@ -54,15 +70,21 @@
 			echo $GLOBALS['HTML']->listTableTop ($title_arr);
 
 			for ($i=0; $i < $rows; $i++) {
-				echo '<tr '. $GLOBALS['HTML']->boxGetAltRowStyle($i) .'><td><pre>
-'._('Date').': '. date(_('Y-m-d H:i'),db_result($result, $i, 'adddate')) .'
-'._('Sender').': ';
+				echo '<tr '. $GLOBALS['HTML']->boxGetAltRowStyle($i) .'><td>'.
+					_('Date').': '.
+					date(_('Y-m-d H:i'),db_result($result, $i, 'adddate')) .'<br />'.
+					_('Sender').': ';
 				if(db_result($result,$i,'user_id') == 100) {
 					echo db_result($result,$i,'realname');
 				} else {
 					echo util_make_link_u (db_result($result,$i,'user_name'),db_result($result,$i,'user_id'),db_result($result,$i,'realname'));
 				}
-				echo "\n\n". util_line_wrap ( db_result($result, $i, 'body'),65,"\n"). '</pre></td></tr>';
+
+				$text = db_result($result, $i, 'body');
+				$text = util_gen_cross_ref($text, $this->ArtifactType->Group->getID());
+				//$text = util_line_wrap( $text, 120,"\n");
+				$text = preg_replace('/\r?\n/', '<br />', $text);
+				echo "<br /><br />".$text.'</td></tr>';
 			}
 
 			echo $GLOBALS['HTML']->listTableBottom();
@@ -104,9 +126,10 @@
 					echo user_getname(db_result($result, $i, 'old_value'));
 
 				} else if ($field == 'close_date') {
-
-					echo date(_('Y-m-d H:i'),db_result($result, $i, 'old_value'));
-
+					if (db_result($result, $i, 'old_value'))
+						echo date(_('Y-m-d H:i'),db_result($result, $i, 'old_value'));
+					else 
+						echo '<i>None</i>';
 				} else {
 
 					echo db_result($result, $i, 'old_value');
@@ -126,6 +149,56 @@
 
 	}
 
+	function showRelations() {
+		global $Language;
+		
+		$aid = $this->getID();
+		
+		// Search for all relations pointing to this record.
+		$sql = "SELECT *
+		FROM artifact_extra_field_list, artifact_extra_field_data, artifact_group_list, artifact, groups
+		WHERE field_type=9
+		AND artifact_extra_field_list.extra_field_id=artifact_extra_field_data.extra_field_id
+		AND artifact_group_list.group_artifact_id = artifact_extra_field_list.group_artifact_id
+		AND artifact.artifact_id = artifact_extra_field_data.artifact_id
+		AND groups.group_id = artifact_group_list.group_id
+		AND (field_data = '$aid' OR field_data LIKE '$aid %' OR field_data LIKE '% $aid %' OR field_data LIKE '% $aid')
+		ORDER BY artifact_group_list.group_id ASC, name ASC, artifact.artifact_id ASC";
+		$res = db_query($sql);
+		if (db_numrows($res)>0) {
+			?>
+<div class="tabbertab" title="<?php echo _('Backward Relations'); ?>">
+<table border="0" width="80%">
+	<tr>
+		<td colspan="2">
+		<h3><?php echo _('Changes') ?>:</h3>
+		<?php
+		$current = '';
+		$end = '';
+		while ($arr = db_fetch_array($res)) {
+			$title = $arr['group_name'].': '.$arr['name'];
+			if ($title != $current) {
+				echo $end.'<strong>'.$title.'</strong>';
+				$current = $title;
+				$end = '<br /><br />';
+			}
+			$text = '[#'.$arr['artifact_id'].']';
+			$url = '/tracker/?func=detail&amp;aid='.$arr['artifact_id'].'&amp;group_id='.$arr['group_id'].'&amp;atid='.$arr['group_artifact_id'];
+			$arg = 'title="'.$arr['summary'].'"' ;
+			if ($arr['status_id'] == 2) {
+				$arg .= 'class="artifact_closed"';
+			}
+			print '<br/>&nbsp;&nbsp;&nbsp;<a href="'.$url.'" '.$arg.'>'.$text.'</a>'.' <a href="'.$url.'">'.$arr['summary'].'</a> <i>(Relation: '.$arr['field_name'].')</i>';
+		}
+		?></td>
+	</tr>
+</table>
+</div>
+<?php
+}	
+	}
+
+	
 }
 
 // Local Variables:

Modified: trunk/gforge/www/tracker/include/ArtifactTypeHtml.class.php
===================================================================
--- trunk/gforge/www/tracker/include/ArtifactTypeHtml.class.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/include/ArtifactTypeHtml.class.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -13,6 +13,8 @@
 require_once $gfcommon.'tracker/ArtifactType.class.php';
 require_once $gfcommon.'tracker/ArtifactExtraField.class.php';
 require_once $gfcommon.'tracker/ArtifactExtraFieldElement.class.php';
+require_once $gfcommon.'tracker/ArtifactWorkflow.class.php';
+require_once $gfcommon.'include/utils_crossref.php';
 
 class ArtifactTypeHtml extends ArtifactType {
 
@@ -55,20 +57,28 @@
 		if (session_loggedin()) {
 			$labels[] = _('Reporting');
 			$links[]  = '/tracker/reporting/?group_id='.$group_id.'&amp;atid='. $this->getID();
-			if ($this->isMonitoring()) {
+  			if ($this->isMonitoring()) {
 				$labels[] = _('Stop Monitor');
-			} else {
+   				$links[]  = '/tracker/?group_id='.$group_id.'&amp;atid='. $this->getID().'&amp;func=monitor&amp;stop=1';
+  			} else {
 				$labels[] = _('Monitor');
-			}
-			$links[]  = '/tracker/?group_id='.$group_id.'&amp;atid='. $this->getID().'&amp;func=monitor';
+ 				$links[]  = '/tracker/?group_id='.$group_id.'&amp;atid='. $this->getID().'&amp;func=monitor&amp;start=1';
+  			}
 
 			if ($this->userIsAdmin()) {
 				$labels[] = _('Admin');
 				$links[]  = '/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID();
 			}
+		} else {
+			$labels[] = _('Monitor');
+			$links[]  = '/tracker/?group_id='.$group_id.'&amp;atid='. $this->getID().'&amp;func=monitor&amp;start=1';	
 		}
 
 		echo $HTML->subMenu($labels,$links);
+		
+		if ($this)
+			plugin_hook ("blocks", "tracker_".$this->getName());
+		
 	}
 
 	function footer($params) {
@@ -83,12 +93,18 @@
 		$links_arr[]='/tracker/admin/?group_id='.$group_id;
 		$title_arr[]=_('New Tracker');
 
-		$links_arr[]='/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID().'&update_type=1';
+		$links_arr[]='/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID().'&amp;update_type=1';
 		$title_arr[]=_('Update Settings');
 
 		$links_arr[]='/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID().'&amp;add_extrafield=1';
 		$title_arr[]=_('Manage Custom Fields');
 
+		$links_arr[]='/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID().'&amp;workflow=1';
+		$title_arr[]=_('Manage Workflow');
+
+		$links_arr[]='/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID().'&amp;customize_list=1';
+		$title_arr[]=_('Customize List');
+
 		$links_arr[]='/tracker/admin/?group_id='.$group_id.'&amp;atid='.$this->getID().'&amp;clone_tracker=1';
 		$title_arr[]=_('Clone Tracker');
 
@@ -105,21 +121,86 @@
 		echo $this->footer($params);
 	}
 
+	function renderSubmitInstructions() {
+		$msg = $this->getSubmitInstructions();
+		return str_replace("\n","<br />", $msg);
+	}
+
+	function renderBrowseInstructions() {
+		$msg = $this->getBrowseInstructions();
+		return str_replace("\n","<br />", $msg);
+	}
+
 	function renderExtraFields($selected=array(),$show_100=false,$text_100='none',$show_any=false,$text_any='Any',$filter='',$status_show_100=false,$mode='') {
+		global $Language;
+		
 		$efarr = $this->getExtraFields($filter);
 		//each two columns, we'll reset this and start a new row
-//TODO - add code for "display only" such as mod-limited and detail.php pages
-		$template = $this->getRenderHTML($filter);
+
+		$template = $this->getRenderHTML($filter, $mode);
+
 		if ($mode=='QUERY') {
 			$keys=array_keys($efarr);
 			for ($k=0; $k<count($keys); $k++) {
 				$i=$keys[$k];
-				$efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTISELECT;
+				if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
+					$efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_MULTISELECT;
+				} else {
+					$efarr[$i]['field_type'] = ARTIFACT_EXTRAFIELDTYPE_TEXT;
+				}
 			}
 		}
+		
+		// 'DISPLAY' mode is for renderding in 'read-only' mode (for detail view).
+		if ($mode === 'DISPLAY') {
+			$keys=array_keys($efarr);
+			for ($k=0; $k<count($keys); $k++) {
+				$i=$keys[$k];
+				$value = $selected[$efarr[$i]['extra_field_id']];
+				if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
+					if ($value == 100) {
+						$value = 'None';
+					} else {
+						$arr =& $this->getExtraFieldElements($efarr[$i]['extra_field_id']);
+						
+						// Convert the values (ids) to names in the ids order.
+						$new = array();
+						for ($j=0; $j<count($arr); $j++) {
+							if (is_array($value)) {
+								if (in_array($arr[$j]['element_id'],$value))
+									$new[]= $arr[$j]['element_name'];
+							} elseif ($arr[$j]['element_id'] === $value) {
+									$new[] = $arr[$j]['element_name'];
+							}
+						}
+						$value = join('<br />', $new);
+					}
+				} else if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT ||
+					$efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
+					$value = preg_replace('/((http|https|ftp):\/\/\S+)/', 
+								"<a href=\"\\1\" target=\"_blank\">\\1</a>", $value);
+				} else if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
+					// Convert artifact id to links.
+					$value = preg_replace('/\b(\d+)\b/e', "_artifactid2url('\\1')", $value);
+				}
+				$template = str_replace('<!--'.$efarr[$i]['field_name'].'-->',$value,$template);		
+			}
+			echo $template;
+			return ;
+		}
+		
 		$keys=array_keys($efarr);
 		for ($k=0; $k<count($keys); $k++) {
 			$i=$keys[$k];
+			$post_name = '';
 
 			if (!isset($selected[$efarr[$i]['extra_field_id']])) 
 				$selected[$efarr[$i]['extra_field_id']] = '';
@@ -138,20 +219,48 @@
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
 
 				$str = $this->renderTextField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2']);
-
+				if ($mode == 'QUERY') {
+					$post_name =  ' <i>'._('(% for wildcards)').'</i>&nbsp;&nbsp;&nbsp;';
+				}
+				
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
 
 				$str = $this->renderTextArea($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2']);
-
+				if ($mode == 'QUERY') {
+					$post_name =  ' <i>'._('(% for wildcards)').'</i>&nbsp;&nbsp;&nbsp;';
+				}
+				
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
 
 				$str = $this->renderMultiSelectBox ($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$show_100,$text_100);
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
 
-				$str = $this->renderSelect($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$status_show_100,$text_100,$show_any,$text_any);
+				// Get the allowed values from the workflow.
+				$atw = new ArtifactWorkflow($this, $efarr[$i]['extra_field_id']);
 
+				// Special treatement for the initial step (Submit).
+				// In this case, the initial value is the first value.
+				if ($selected === true) {
+					$selected_node = 100;
+				} elseif (isset($selected[$efarr[$i]['extra_field_id']]) && $selected[$efarr[$i]['extra_field_id']]) {
+					$selected_node = $selected[$efarr[$i]['extra_field_id']];
+				} else {
+					$selected_node = 100;
+				}
+
+				$allowed = $atw->getNextNodes($selected_node);
+				$allowed[] = $selected_node;
+				$str = $this->renderSelect($efarr[$i]['extra_field_id'],$selected_node,$status_show_100,$text_100,$show_any,$text_any, $allowed);
+
+			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
+
+				$str = $this->renderRelationField($efarr[$i]['extra_field_id'],$selected[$efarr[$i]['extra_field_id']],$efarr[$i]['attribute1'],$efarr[$i]['attribute2']);
+				if ($mode == 'UPDATE') {
+					$post_name = html_image('ic/forum_edit.gif','37','15',array('title'=>"Click to edit", 'alt'=>"Click to edit", 'onclick'=>"switch2edit(this, 'show$i', 'edit$i')", 'border'=>"0"));
+				}
 			}
+			$template = str_replace('<!--PostName:'.$efarr[$i]['field_name'].'-->',$post_name,$template);
 			$template = str_replace('<!--'.$efarr[$i]['field_name'].'-->',$str,$template);
 		}
 		if($template != NULL){
@@ -164,8 +273,14 @@
 	 *
 	 *	@return	string	HTML template.
 	 */
-	function getRenderHTML($filter='') {
-		return (($this->data_array['custom_renderer']) ? $this->data_array['custom_renderer'] : $this->generateRenderHTML($filter) );
+	function getRenderHTML($filter='', $mode='') {
+		// Use template only for the browse (not for query or mass update)
+		if (($mode === 'DISPLAY' || $mode === 'DETAIL' || $mode === 'UPDATE') 
+			&& $this->data_array['custom_renderer']) {
+			return $this->data_array['custom_renderer'];
+		} else {
+			return $this->generateRenderHTML($filter, $mode);
+		}
 	}
 
 	/**
@@ -173,7 +288,9 @@
 	 *
 	 *	@return	string	HTML template.
 	 */
-	function generateRenderHTML($filter='') {
+	function generateRenderHTML($filter='', $mode) {
+		global $Language;
+		
 		$efarr =& $this->getExtraFields($filter);
 		//each two columns, we'll reset this and start a new row
 
@@ -185,23 +302,30 @@
 
 		$keys=array_keys($efarr);
 		$count=count($keys);
+		if ($count == 0) return '';
+		
 		for ($k=0; $k<$count; $k++) {
 			$i=$keys[$k];
 
+			// Do not show the required star in query mode (creating/updating a query).
+			$is_required = ($mode == 'QUERY' || $mode == 'DISPLAY') ?	0 : $efarr[$i]['is_required'];
+			$name = $efarr[$i]['field_name'].($is_required ? utils_requiredField() : '').': ';
+			$name = '<strong>'.$name.'</strong>';
+			
 			if ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_SELECT) {
 
 				$return .= '
-					<td width="50%" valign="top"><strong>'.$efarr[$i]['field_name'].':<br /></strong><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="50%" valign="top">'.$name.'<br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_CHECKBOX) {
 
 				$return .= '
-					<td width="50%" valign="top"><strong>'.$efarr[$i]['field_name'].':<br /></strong><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="50%" valign="top">'.$name.'<br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RADIO) {
 
 				$return .= '
-					<td width="50%" valign="top"><strong>'.$efarr[$i]['field_name'].':<br /></strong><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="50%" valign="top">'.$name.'<br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXT) {
 
@@ -216,7 +340,7 @@
 					$colspan=1;
 				}
 				$return .= '
-					<td width="'.(50*$colspan).'%" colspan="'.$colspan.'" valign="top"><strong>'.$efarr[$i]['field_name'].'</strong>:<br /><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="'.(50*$colspan).'%" colspan="'.$colspan.'" valign="top">'.$name.'<!--PostName:'.$efarr[$i]['field_name'].'--><br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_TEXTAREA) {
 
@@ -231,18 +355,33 @@
 					$colspan=1;
 				}
 				$return .= '
-					<td width="'.(50*$colspan).'%" colspan="'.$colspan.'" valign="top"><strong>'.$efarr[$i]['field_name'].'</strong>:<br /><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="'.(50*$colspan).'%" colspan="'.$colspan.'" valign="top">'.$name.'<!--PostName:'.$efarr[$i]['field_name'].'--><br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_MULTISELECT) {
 
 				$return .= '
-					<td width="50%" valign="top"><strong>'.$efarr[$i]['field_name'].'</strong>:<br /><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="50%" valign="top">'.$name.'<br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
 			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_STATUS) {
 
 				$return .= '
-					<td width="50%" valign="top"><strong>'.$efarr[$i]['field_name'].'</strong>:<br /><!--'.$efarr[$i]['field_name'].'--></td>';
+					<td width="50%" valign="top">'.$name.'<br /><!--'.$efarr[$i]['field_name'].'--></td>';
 
+			} elseif ($efarr[$i]['field_type'] == ARTIFACT_EXTRAFIELDTYPE_RELATION) {
+
+				//text fields might be really wide, so need a row to themselves.
+				if (($col_count == 1) && ($efarr[$i]['attribute1'] > 30)) {
+					$colspan=2;
+					$return .= '
+					<td>&nbsp;</td>
+			</tr>
+			<tr>';
+				} else {
+					$colspan=1;
+				}
+				$return .= '
+					<td width="'.(50*$colspan).'%" colspan="'.$colspan.'" valign="top">'.$name.'<!--PostName:'.$efarr[$i]['field_name'].'--><br /><!--'.$efarr[$i]['field_name'].'--></td>';
+
 			}
 			$col_count++;
 			//we've done two columns - if there are more to do, start a new row
@@ -273,7 +412,7 @@
 	 *	@param		string	What to call the '100 row'
 	 *	@return		box and choices	
 	 */	
-	function renderSelect ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any') {
+	function renderSelect ($extra_field_id,$checked='xzxz',$show_100=false,$text_100='none',$show_any=false,$text_any='Any', $allowed=false) {
 		if ($text_100 == 'none'){
 			$text_100=_('None');
 		}
@@ -282,7 +421,7 @@
 			$keys[$i]=$arr[$i]['element_id'];
 			$vals[$i]=$arr[$i]['element_name'];
 		}
-		return html_build_select_box_from_arrays ($keys,$vals,'extra_fields['.$extra_field_id.']',$checked,$show_100,$text_100,$show_any,$text_any);
+		return html_build_select_box_from_arrays ($keys,$vals,'extra_fields['.$extra_field_id.']',$checked,$show_100,$text_100,$show_any,$text_any, $allowed);
 	}
 
 	/**
@@ -323,12 +462,12 @@
 		if ($show_100) {
 			$return .= '
 				<input type="checkbox" name="extra_fields['.$extra_field_id.'][]" value="100" '.
-			((in_array(100,$checked)) ? 'CHECKED' : '').'/>'.$text_100.'<br />';
+			((in_array(100,$checked)) ? 'checked="checked"' : '').'/>&nbsp;'.$text_100.'<br />';
 		}
 		for ($i=0; $i<count($arr); $i++) {
 			$return .= '
 				<input type="checkbox" name="extra_fields['.$extra_field_id.'][]" value="'.$arr[$i]['element_id'].'" '.
-			((in_array($arr[$i]['element_id'],$checked)) ? 'CHECKED' : '').'/>&nbsp;'.$arr[$i]['element_name'].'<br />';
+			((in_array($arr[$i]['element_id'],$checked)) ? 'checked="checked"' : '').'/>&nbsp;'.$arr[$i]['element_name'].'<br />';
 		}
 		return $return;
 	}
@@ -357,7 +496,8 @@
 			$keys[]=$arr[$i]['element_id'];
 			$vals[]=$arr[$i]['element_name'];
 		}
-		return html_build_multiple_select_box_from_arrays($keys,$vals,"extra_fields[$extra_field_id][]",$checked,15,$show_100,$text_100);
+		$size = min( count($arr)+1, 15);
+			return html_build_multiple_select_box_from_arrays($keys,$vals,"extra_fields[$extra_field_id][]",$checked,$size,$show_100,$text_100);
 	}
 
 	/**
@@ -373,6 +513,29 @@
 	}
 
 	/**
+	 *	renderRelationField - this function builds a relation field.
+	 *	
+	 *	@param		int 	The ID of this field.
+	 *	@param 		string	The data for this field.
+	 *	@return		text area and data.
+	 */	
+	function renderRelationField ($extra_field_id,$contents,$size,$maxlength) {
+		global $Language;
+		$arr =& $this->getExtraFieldElements($extra_field_id);
+		for ($i=0; $i<count($arr); $i++) {
+			$keys[$i]=$arr[$i]['element_id'];
+			$vals[$i]=$arr[$i]['element_name'];
+		}
+		// Convert artifact id to links.
+		$html_contents = preg_replace('/\b(\d+)\b/e', "_artifactid2url('\\1','title')", $contents);
+		$edit_contents = $this->renderTextField ($extra_field_id,$contents,$size,$maxlength);
+		$edit_tips = '<br/><span class="tips">'._('Tip: Enter a space-separated list of artifact ids ([#NNN] also accepted)').'</span>';
+		return '
+			<div id="edit'.$extra_field_id.'" style="display: none;">'.$edit_contents.$edit_tips.'</div>
+			<div id="show'.$extra_field_id.'" style="display: block;">'.$html_contents.'</div>';
+	}
+
+	/**
 	 *	renderTextArea - this function builds a text area.
 	 *	
 	 *	@param		int 	The ID of this field.
@@ -402,7 +565,8 @@
 			if (!is_array($checked)) {
 				$checked = explode(',',$checked);
 			}
-			return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,15,$show_100,$text_100);
+			$size = min( count($ids)+1, 15);
+			return html_build_multiple_select_box_from_arrays ($ids,$names,$name,$checked,$size,$show_100,$text_100);
 		} else {
 			return html_build_select_box_from_arrays ($ids,$names,$name,$checked,$show_100,$text_100);
 		}

Modified: trunk/gforge/www/tracker/ind.php
===================================================================
--- trunk/gforge/www/tracker/ind.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/ind.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -35,8 +35,10 @@
 	echo "<p><strong>".sprintf(_('No trackers have been set up, or you cannot view them.<p><span class="important">The Admin for this project will have to set up data types using the %1$s admin page %2$s</span>'), '<a href="'.util_make_url ('/tracker/admin/?group_id='.$group_id).'">', '</a>')."</strong>";
 } else {
 
-	echo '<p>'._('Choose a tracker and you can browse/edit/add items to it.').'<p>';
+	plugin_hook ("blocks", "tracker index");
 
+	echo '<p>'._('Choose a tracker and you can browse/edit/add items to it.').'</p>';
+
 	/*
 		Put the result set (list of trackers for this group) into a column with folders
 		*/
@@ -52,8 +54,8 @@
 			echo '
 		<tr '. $HTML->boxGetAltRowStyle($j) . '>
 			<td><a href="'.util_make_url ('/tracker/?atid='.$at_arr[$j]->getID().'&amp;group_id='.$group_id.'&func=browse').'">'.
-			html_image("ic/tracker20w.png","20","20",array("border"=>"0")).' &nbsp;'.
-			$at_arr[$j]->getName() .'</a>
+ 				html_image("ic/tracker20w.png","20","20",array("border"=>"0", "align"=>"middle")).' &nbsp;'.
+				$at_arr[$j]->getName() .'</a>
 			</td>
 			<td>' .  $at_arr[$j]->getDescription() .'
 			</td>

Modified: trunk/gforge/www/tracker/mod-limited.php
===================================================================
--- trunk/gforge/www/tracker/mod-limited.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/mod-limited.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -14,9 +14,10 @@
 ?>
 	<h3>[#<?php echo $ah->getID(); ?>] <?php echo $ah->getSummary(); ?></h3>
 
-	<form action="<?php echo getStringFromServer('PHP_SELF'); ?>?group_id=<?php echo $group_id; ?>&amp;atid=<?php echo $ath->getID(); ?>" METHOD="post" enctype="multipart/form-data">
+	<form action="<?php echo getStringFromServer('PHP_SELF'); ?>?group_id=<?php echo $group_id; ?>&amp;atid=<?php echo $ath->getID(); ?>" enctype="multipart/form-data" method="post">
 	<input type="hidden" name="form_key" value="<?php echo form_generate_key(); ?>" />
 	<input type="hidden" name="func" value="postmod" />
+	<input type="hidden" name="MAX_FILE_SIZE" value="10000000" />
 	<input type="hidden" name="artifact_id" value="<?php echo $ah->getID(); ?>" />
 
 	<table width="80%">
@@ -27,7 +28,7 @@
 			<td><?php
 				if ($ah->isMonitoring()) {
 					$img="xmail16w.png";
-					$key="stop_monitoring";
+					$key="monitorstop";
 				} else {
 					$img="mail16w.png";
 					$key="monitor";
@@ -35,7 +36,7 @@
 				echo '
 				<a href="index.php?group_id='.$group_id.'&amp;artifact_id='.$ah->getID().'&amp;atid='.$ath->getID().'&amp;func=monitor"><strong>'.
 					html_image('ic/'.$img.'','20','20',array()).' '.$key.'</strong></a>';
-				?>&nbsp;<a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=monitor'); ?>')"><strong>(?)</strong></a>
+				?>&nbsp;<a href="javascript:help_window('/help/tracker.php?helpname=monitor')"><strong>(?)</strong></a>
 			</td>
 			<td><?php
 				if ($group->usesPM()) {
@@ -49,7 +50,8 @@
 				<input type="submit" name="submit" value="<?php echo _('Submit') ?>" />
 			</td>
 		</tr>
-	</table>
+</table>
+<p/>
 <?php } ?>
 <table border="0" width="80%">
 
@@ -78,8 +80,8 @@
 	</tr>
 
     <?php
-        $ath->renderExtraFields($ah->getExtraFieldData(),true);
-    ?>
+		$ath->renderExtraFields($ah->getExtraFieldData(),true,'none',false,'Any','',false,'UPDATE');
+		?>
 
 	<tr>
 		<td><strong><?php echo _('Assigned to')?>: <a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=assignee'); ?>')"><strong>(?)</strong></a></strong><br />
@@ -110,7 +112,7 @@
 	</tr>
 
 	<tr>
-		<td colspan="2"><strong><?php echo _('Summary')?>: <a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=summary'); ?>')"><strong>(?)</strong></a></strong><br />
+		<td colspan="2"><strong><?php echo _('Summary')?><?php echo utils_requiredField(); ?>: <a href="javascript:help_window('/help/tracker.php?helpname=summary')"><strong>(?)</strong></a></strong><br />
 			<?php echo $ah->getSummary(); ?>
 		</td>
 	</tr>
@@ -126,7 +128,7 @@
 <table border="0" width="80%">
 	<tr><td colspan="2">
 		<br /><strong><?php echo _('OR Attach A Comment') ?>: <?php echo notepad_button('document.forms[1].details') ?> <a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=comment'); ?>')"><strong>(?)</strong></a></strong><br />
-		<textarea name="details" rows="7" cols="60"></textarea></p>
+		<textarea name="details" rows="7" cols="60"></textarea>
 		<p>
 		<h3><?php echo _('Followup') ?>:</h3>
 		<?php
@@ -165,7 +167,7 @@
 
 			for ($i=0; $i<$count; $i++) {
 				echo '<tr '. $GLOBALS['HTML']->boxGetAltRowStyle($i) .'>
-				<td><input type="CHECKBOX" name="delete_file[]" value="'. $file_list[$i]->getID() .'">'._('Delete').' </td>
+				<td><input type="checkbox" name="delete_file[]" value="'. $file_list[$i]->getID() .'">'._('Delete').' </td>
 				<td>'. htmlspecialchars($file_list[$i]->getName()) .'</td>
 				<td>'.  htmlspecialchars($file_list[$i]->getDescription()) .'</td>
 				<td>'.util_make_link ('/tracker/download.php/'.$group_id.'/'. $ath->getID().'/'. $ah->getID() .'/'.$file_list[$i]->getID().'/'.$file_list[$i]->getName(),_('Download')).'</td>
@@ -173,7 +175,7 @@
 			}
 
 		} else {
-			echo '<tr '.$GLOBALS['HTML']->boxGetAltRowStyle(0).'><td colspan=3>'._('No Files Currently Attached').'</td></tr>';
+			echo '<tr '.$GLOBALS['HTML']->boxGetAltRowStyle(0).'><td colspan="4">'._('No Files Currently Attached').'</td></tr>';
 		}
 		echo $GLOBALS['HTML']->listTableBottom();
 		?>
@@ -198,6 +200,7 @@
 	</td></tr>
 </table>
 </div>
+<?php $ah->showRelations(); ?>
 </div>
 		</form>
 <?php

Modified: trunk/gforge/www/tracker/mod.php
===================================================================
--- trunk/gforge/www/tracker/mod.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/mod.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -19,6 +19,7 @@
 	<form action="<?php echo getStringFromServer('PHP_SELF'); ?>?group_id=<?php echo $group_id; ?>&amp;atid=<?php echo $ath->getID(); ?>"  enctype="multipart/form-data" method="post">
 	<input type="hidden" name="form_key" value="<?php echo form_generate_key(); ?>"/>
 	<input type="hidden" name="func" value="postmod"/>
+	<input type="hidden" name="MAX_FILE_SIZE" value="10000000" />
 	<input type="hidden" name="artifact_id" value="<?php echo $ah->getID(); ?>"/>
 
 	<table width="80%">
@@ -57,7 +58,7 @@
 			</td>
 		</tr>
 </table>
-<br />
+<p/>
 <?php } ?>
 <table border="0" width="80%">
 	<tr>
@@ -117,7 +118,7 @@
 	</tr>
 
 	<?php
-		$ath->renderExtraFields($ah->getExtraFieldData(),true);
+		$ath->renderExtraFields($ah->getExtraFieldData(),true,'none',false,'Any','',false,'UPDATE');
 	?>
 
 	<tr>
@@ -152,7 +153,7 @@
 	</tr>
 
 	<tr>
-		<td><strong><?php echo _('Summary')?>: <a href="javascript:help_window('<?php echo util_make_url ('/help/tracker.php?helpname=summary'); ?>')"><strong>(?)</strong></a></strong><br />
+		<td><strong><?php echo _('Summary')?><?php echo utils_requiredField(); ?>: <a href="javascript:help_window('/help/tracker.php?helpname=summary')"><strong>(?)</strong></a></strong><br />
 		<input type="text" name="summary" size="70" value="<?php
 			echo $ah->getSummary(); 
 			?>" maxlength="255" />
@@ -161,7 +162,14 @@
 		</td>
 	</tr>
 	<tr><td colspan="2">
-		<?php echo $ah->showDetails(); ?>
+		<div id="edit" style="display:none;">
+		<strong><?php echo _('Detailed description') ?><?php echo utils_requiredField(); ?>: <?php echo notepad_button('document.forms[1].details') ?> <a href="javascript:help_window('/help/tracker.php?helpname=description')">(?)</a></strong>
+		<br />
+		<textarea name="description" rows="30" cols="79"><?php echo $ah->getDetails(); ?></textarea>
+		</div>
+		<div id="show" style="display:block;">
+		<?php echo $ah->showDetails(true); ?>
+		</div>
 	</td></tr>
 </table>
 <br />
@@ -256,7 +264,7 @@
 			for ($i=0; $i<$count; $i++) {
 				echo '
 				<tr '. $GLOBALS['HTML']->boxGetAltRowStyle($i) .'>
-				<td><input type="CHECKBOX" name="delete_file[]" value="'. $file_list[$i]->getID() .'">'._('Delete').' </td>'.
+				<td><input type="checkbox" name="delete_file[]" value="'. $file_list[$i]->getID() .'">'._('Delete').' </td>'.
 				'<td>'. htmlspecialchars($file_list[$i]->getName()) .'</td>
 				<td>'.util_make_link ('/tracker/download.php/'.$group_id.'/'. $ath->getID().'/'. $ah->getID() .'/'.$file_list[$i]->getID().'/'.$file_list[$i]->getName(),_('Download')).'</td>
 				</tr>';
@@ -289,6 +297,7 @@
 	</td></tr>
 </table>
 </div>
+<?php $ah->showRelations(); ?>
 </div>
 		</form>
 

Modified: trunk/gforge/www/tracker/query.php
===================================================================
--- trunk/gforge/www/tracker/query.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/query.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -41,7 +41,8 @@
 		if (!$aq || !is_object($aq)) {
 			exit_error('Error',$aq->getErrorMessage());
 		}
-		$query_name = getStringFromRequest('query_name');
+		$query_name = trim(getStringFromRequest('query_name'));
+		$query_type = getStringFromRequest('query_type',0);
 		$_status = getStringFromRequest('_status');
 		$_assigned_to = getStringFromRequest('_assigned_to');
 		$_sort_col = getStringFromRequest('_sort_col');
@@ -50,13 +51,18 @@
 		$_moddaterange = getStringFromRequest('_moddaterange');
 		$_opendaterange = getStringFromRequest('_opendaterange');
 		$_closedaterange = getStringFromRequest('_closedaterange');
-		if (!$aq->create($query_name,$_status,$_assigned_to,$_moddaterange,$_sort_col,$_sort_ord,$extra_fields,$_opendaterange,$_closedaterange)) {
+		$_summary = getStringFromRequest('_summary');
+		$_description = getStringFromRequest('_description');
+		$_followups = getStringFromRequest('_followups');
+		if (!$aq->create($query_name,$_status,$_assigned_to,$_moddaterange,$_sort_col,$_sort_ord,$extra_fields,$_opendaterange,$_closedaterange,$_summary,$_description,$_followups,$query_type)) {
 			exit_error('Error',$aq->getErrorMessage());
 		} else {
 			$feedback .= 'Successfully Created';
 		}
 		$aq->makeDefault();
 		$query_id=$aq->getID();
+		header('Location: /tracker/?atid='.$atid.'&group_id='.$group_id.'&func=browse');
+		exit;
 	//	
 /*
 	// Make the displayed query the default
@@ -83,6 +89,7 @@
 			exit_error('Error',$aq->getErrorMessage());
 		}
 		$query_name = getStringFromRequest('query_name');
+		$query_type = getStringFromRequest('query_type',0);
 		$_status = getStringFromRequest('_status');
 		$_assigned_to = getStringFromRequest('_assigned_to');
 		$_sort_col = getStringFromRequest('_sort_col');
@@ -90,14 +97,19 @@
 		$_moddaterange = getStringFromRequest('_moddaterange');
 		$_opendaterange = getStringFromRequest('_opendaterange');
 		$_closedaterange = getStringFromRequest('_closedaterange');
+		$_summary = getStringFromRequest('_summary');
+		$_description = getStringFromRequest('_description');
+		$_followups = getStringFromRequest('_followups');
 		$extra_fields = getStringFromRequest('extra_fields');
-		if (!$aq->update($query_name,$_status,$_assigned_to,$_moddaterange,$_sort_col,$_sort_ord,$extra_fields,$_opendaterange,$_closedaterange)) {
+		if (!$aq->update($query_name,$_status,$_assigned_to,$_moddaterange,$_sort_col,$_sort_ord,$extra_fields,$_opendaterange,$_closedaterange,$_summary,$_description,$_followups,$query_type)) {
 			exit_error('Error',$aq->getErrorMessage());
 		} else {
 			$feedback .= 'Query Updated';
 		}
 		$aq->makeDefault();
 		$query_id=$aq->getID();
+		header('Location: /tracker/?atid='.$atid.'&group_id='.$group_id.'&func=browse');
+		exit;
 	//
 	//	Just load the query
 	//
@@ -124,6 +136,8 @@
 			$feedback .= 'Query Deleted';
 		}
 		$query_id=0;
+		header('Location: /tracker/?atid='.$atid.'&group_id='.$group_id.'&func=browse');
+		exit;
 	}	
 } else {
 	$user=session_get_user();
@@ -146,6 +160,10 @@
 $_moddaterange=$aq->getModDateRange();
 $_opendaterange=$aq->getOpenDateRange();
 $_closedaterange=$aq->getCloseDateRange();
+$_summary=$aq->getSummary();
+$_description=$aq->getDescription();
+$_followups=$aq->getFollowups();
+$query_type=$aq->getQueryType();
 //
 //	creating a custom technician box which includes "any" and "unassigned"
 $tech_box=$ath->technicianBox ('_assigned_to[]',$_assigned_to,true,'none','-1',false,true);
@@ -211,27 +229,19 @@
 
 //	Show the new pop-up boxes to select assigned to, status, etc
 //
-?><html>
-<head>
-<title>Query</title>
-<link rel="stylesheet" type="text/css" href="<?php echo util_make_url ('/themes/css/gforge-compat.css'); ?>" />
-<?php
+$ath->header(array('atid'=>$ath->getID()));
 
-$theme_cssfile=$GLOBALS['sys_themeroot'].$GLOBALS['sys_theme'].'/css/theme.css';
-if (file_exists($theme_cssfile)){
+echo '<table align="center"><tr><td>' .
+		'<fieldset><legend>'.
+		_('Build Query').
+		'</legend>';
+
 echo '
-<link rel="stylesheet" type="text/css" href="'.util_make_url ('/themes/'.$GLOBALS['sys_theme'].'/css/theme.css').'" />
-';
-}
-echo '
-
-</head>
-<body>
 <h1>'. $feedback .'</h1>
 
-<table border="3" cellpadding="4" rules="groups" frame="box" width="100%" class="tablecontent">
-	<form action="'.getStringFromServer('PHP_SELF').'?func=query&group_id='.$group_id.'&atid='.$ath->getID().'" method="post">
-	<input type="hidden" name="form_key" value="'.form_generate_key().'">
+<form action="'.getStringFromServer('PHP_SELF').'?func=query&amp;group_id='.$group_id.'&amp;atid='.$ath->getID().'" method="post">
+<input type="hidden" name="form_key" value="'.form_generate_key().'" />
+<table align="center" border="3" cellpadding="4" rules="groups" frame="box" width="100%" class="tablecontent">
 	<tr>
 		<td>
 			<input type="submit" name="submit" value="'._('Save Changes').'" />
@@ -240,20 +250,21 @@
 	if(db_numrows($res)>0) {
 		echo html_build_select_box($res,'query_id',$query_id,false).'';
 	}
+
 	echo '
 		</td>
 	</tr>
 	<tr class="tablecontent">
-		<td>
-		<input type="radio" name="query_action" value="1" '.((!$query_id) ? 'checked' : '' ).'>'._('Name and Save Query').'<br />';
+		<td>';
 	if(db_numrows($res)>0) {
 		echo '
-		<input type="radio" name="query_action" value="4">'._('Load Query').'<br />';
-	}
-	if ($query_id) {
+		<input type="radio" name="query_action" value="1" />'._('Name and Save Query').'<br />
+		<input type="radio" name="query_action" value="4" />'._('Load Query').'<br />
+		<input type="radio" name="query_action" value="3" checked="checked" />'._('Update Query').'<br />
+		<input type="radio" name="query_action" value="5" />'._('Delete Query');
+	} else {
 		echo '
-		<input type="radio" name="query_action" value="3" checked>'._('Update Query').'<br />
-		<input type="radio" name="query_action" value="5">'._('Delete Query').'';
+		<input type="hidden" name="query_action" value="1" />'._('Name and Save Query').'<br />';
 	}
 	echo '
 		</td>
@@ -263,36 +274,79 @@
 </table>';
 
 echo'
-<table width="100%" class="tablecontent">
+<table width="100%" class="tablecontent">';
+if ($ath->userIsAdmin()) {
+	$sql = "SELECT query_name 
+			FROM artifact_query WHERE query_type=2 AND group_artifact_id='".$ath->getID()."'";
+	$default_query = db_result(db_query($sql),0, 'query_name');
+	if ($default_query) {
+		if ($default_query == $aq->getName()) {
+			$note = '';
+		} else {
+			$note= '<br/><i>'.sprintf(_('Note: The default project query is currently \'%1$s\'.'), $default_query).'</i>';
+		}
+	} else {
+		$note= '<br/><i>'._('Note: There is no default project query defined.').'</i>';
+	}
+	echo '
 	<tr>
-		<td>'._('Assignee').'</a><br />'. $tech_box .'</td>
-		<td>';
+		<td colspan="2">
+			<strong>'._('Type of query').':</strong><br />
+			<input name="query_type" value="0" type="radio"'.(($query_type==0) ? ' checked="checked"' : '' ).' />'.
+			_('Private query').'<br />
+			<input name="query_type" value="1" type="radio"'.(($query_type==1) ? ' checked="checked"' : '' ).' />'.
+			_('Project level query (query is public)').'<br />
+			<input name="query_type" value="2" type="radio"'.(($query_type==2) ? ' checked="checked"' : '' ).' />'.
+			_('Default project query (for project level query only)').'<br />
+			'.$note.'
+			<hr/>
+		</td>
+	</tr>';
+}
+	echo '<tr>
+		<td><strong>'._('Assignee').':</strong><br />'. $tech_box .'</td>
+		<td valign="top">';
 		if (!$ath->usesCustomStatuses()) {
-			echo _('State').':&nbsp;<br />'. $ath->statusBox('_status',$_status,true,_('Any'));
+			echo '<strong>'._('State').':</strong><br />'. $ath->statusBox('_status',$_status,true,_('State'));
 		}
 		echo '</td>
 	</tr>';
-	$ath->renderExtraFields($extra_fields,true,'None',true,'Any',ARTIFACT_EXTRAFIELD_FILTER_INT,false,'QUERY');
+	$ath->renderExtraFields($extra_fields,true,'None',true,'Any','',false,'QUERY');
 	
+	$tips = '<i>'._('(% for wildcards)').'</i>&nbsp;&nbsp;&nbsp;';
+	
 echo '
 	<tr>
+		<td colspan="2" nowrap="nowrap">'.
+		'<strong>'._('Last Modified Date range').':</strong> <i>(YYYY-MM-DD&nbsp;YYYY-MM-DD Format)</i><br />
+		<input type="text" name="_moddaterange" size="21" maxlength="21" value="'. htmlspecialchars($_moddaterange) .'" /><p/>
+		<strong>'._('Open Date range').':</strong> <i>(YYYY-MM-DD&nbsp;YYYY-MM-DD Format)</i><br />
+		<input type="text" name="_opendaterange" size="21" maxlength="21" value="'. htmlspecialchars($_opendaterange) .'" /><p/>
+		<strong>'._('Close Date range').':</strong> <i>(YYYY-MM-DD&nbsp;YYYY-MM-DD Format)</i><br />
+		<input type="text" name="_closedaterange" size="21" maxlength="21" value="'. htmlspecialchars($_closedaterange) .'" />
+		</td>
+	</tr>
+	<tr>
 		<td colspan="2">'.
-		_('Last Modified Date range').':<strong>(YYYY-MM-DD&nbsp;YYYY-MM-DD Format)</strong><br />
-		<input type="text" name="_moddaterange" size="21" maxlength="21" value="'. htmlspecialchars($_moddaterange) .'"><p/>
-		'._('Open Date range').': <strong>(YYYY-MM-DD&nbsp;YYYY-MM-DD Format)</strong><br />
-		<input type="text" name="_opendaterange" size="21" maxlength="21" value="'. htmlspecialchars($_opendaterange) .'"><p/>
-		'._('Close Date range').': <strong>(YYYY-MM-DD&nbsp;YYYY-MM-DD Format)</strong><br />
-		<input type="text" name="_closedaterange" size="21" maxlength="21" value="'. htmlspecialchars($_closedaterange) .'">
+		'<strong>'._('Summary').':</strong> '.$tips.'<br />
+		<input type="text" name="_summary" size="40" value="'. htmlspecialchars($_summary) .'" /><p/>
+		<strong>'._('Detailed description').':</strong> '.$tips.'<br />
+		<input type="text" name="_description" size="40" value="'. htmlspecialchars($_description) .'" /><p/>
+		<strong>'._('Followups').':</strong> '.$tips.'<br />
+		<input type="text" name="_followups" size="40" value="'. htmlspecialchars($_followups) .'" />
 		</td>
 	</tr>
 	<tr>
-		<td>'._('Order by').'<br />
+		<td><strong>'._('Order by').':</strong><br />
 		'. 
 		html_build_select_box_from_arrays($order_arr,$order_name_arr,'_sort_col',$_sort_col,false) .'</td>
 		<td>&nbsp;<br />
 		'.html_build_select_box_from_arrays($sort_arr,$sort_name_arr,'_sort_ord',$_sort_ord,false) .'</td>
-	</tr>
-	</form></table></body></html>';
+	</tr>';
+	echo '
+	</table></form>';
+echo '</fieldset></td></tr></table>';
+$ath->footer(array());
 
 // Local Variables:
 // mode: php

Modified: trunk/gforge/www/tracker/reporting/index.php
===================================================================
--- trunk/gforge/www/tracker/reporting/index.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/reporting/index.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -38,11 +38,30 @@
 $start = getStringFromRequest('start');
 $end = getStringFromRequest('end');
 
+if (!$SPAN)
+	$SPAN=REPORT_TYPE_MONTHLY;
+	
 $report=new Report();
 if ($report->isError()) {
 	exit_error($report->getErrorMessage());
 }
 
+/*
+ * Set the start date to birth of the project.
+ */
+$res=db_query("SELECT register_time FROM groups WHERE group_id=$group_id");
+$report->site_start_date=db_result($res,0,'register_time');
+
+if (!$start) {
+	$z =& $report->getMonthStartArr();
+	$start = $z[0];
+}
+
+if (!$end) {
+	$z =& $report->getMonthStartArr();
+	$end = $z[ count($z)-1];
+}
+
 $group =& group_get_object($group_id);
 if (!$group || !is_object($group)) {
 	exit_error('Error','Error - Could Not Get Group');
@@ -96,37 +115,36 @@
 echo site_project_header($params);
 
 ?>
+<div align="center">
 <h3>Project Activity</h3>
-<p>
 <form action="<?php echo getStringFromServer('PHP_SELF'); ?>" method="get">
-<input type="hidden" name="sw" value="<?php echo $sw; ?>">
-<input type="hidden" name="group_id" value="<?php echo $group_id; ?>">
 <table><tr>
-<td><strong>Tracker:</strong><br /><?php echo html_build_select_box($restracker,'atid',$atid,false); ?></td>
+<td>
+<input type="hidden" name="sw" value="<?php echo $sw; ?>" />
+<input type="hidden" name="group_id" value="<?php echo $group_id; ?>" />
+<strong>Tracker:</strong><br /><?php echo html_build_select_box($restracker,'atid',$atid,false); ?></td>
 <td><strong>Area:</strong><br /><?php echo html_build_select_box_from_arrays($vals, $labels, 'area',$area,false); ?></td>
 <td><strong>Type:</strong><br /><?php echo report_span_box('SPAN',$SPAN,true); ?></td>
 <td><strong>Start:</strong><br /><?php echo report_months_box($report, 'start', $start); ?></td>
 <td><strong>End:</strong><br /><?php echo report_months_box($report, 'end', $end); ?></td>
-<td><input type="submit" name="submit" value="Refresh"></td>
+<td><input type="submit" name="submit" value="Refresh" /></td>
 </tr></table>
 </form>
 <p>
 <?php if ($atid) {
 		if (!$area || $area == 'activity') { 
 	?>
-	<img src="trackeract_graph.php?<?php echo "SPAN=$SPAN&start=$start&end=$end&group_id=$group_id&atid=$atid"; ?>" width="640" height="480">
-	<p>
+	<img src="trackeract_graph.php?<?php echo "SPAN=$SPAN&amp;start=$start&amp;end=$end&amp;group_id=$group_id&amp;atid=$atid"; ?>" width="640" height="480" alt="" />
 	<?php
 		} else {
 	?>
-	<img src="trackerpie_graph.php?<?php echo "SPAN=$SPAN&start=$start&end=$end&group_id=$group_id&atid=$atid&area=$area"; ?>" width="640" height="480">
-	<p>
+	<img src="trackerpie_graph.php?<?php echo "SPAN=$SPAN&amp;start=$start&amp;end=$end&amp;group_id=$group_id&amp;atid=$atid&amp;area=$area"; ?>" width="640" height="480" alt="" />
 	<?php
 
 		}
 
 }
-
-echo site_project_footer(array());
-
 ?>
+</p>
+</div>
+<?php echo site_project_footer(array()); ?>

Modified: trunk/gforge/www/tracker/reporting/trackeract_graph.php
===================================================================
--- trunk/gforge/www/tracker/reporting/trackeract_graph.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/reporting/trackeract_graph.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -63,7 +63,7 @@
 
 // Create the graph. These two calls are always required
 $graph  = new Graph(640, 480,"auto");
-$graph->SetMargin(50,10,35,50);
+$graph->SetMargin(50,10,35,80);
 $graph->SetScale( "textlin");
 //$graph->SetScale( "linlog");
 //$graph ->SetYScale("log");
@@ -87,11 +87,11 @@
 $graph->Add( $lineplot3 );
 
 //	Legends
-$lineplot->SetLegend ("Avg Time Open");
+$lineplot->SetLegend ("Avg Time Open (in days)");
 $lineplot2 ->SetLegend("Total Opened");
 $lineplot3 ->SetLegend("Total Still Open");
 
-//echo "<pre>".print_r($report->getDates()).'<br>'.print_r($ydata).'<br>'.print_r($ydata2).'<br>'.print_r($ydata3);
+//echo "<pre>".print_r($report->getDates()).'<br />'.print_r($ydata).'<br />'.print_r($ydata2).'<br />'.print_r($ydata3);
 //echo "<pre>".print_r($ydata2);
 //exit;
 
@@ -99,7 +99,7 @@
 //	Titles
 //
 $graph->title->Set("Tracker Activity For: ".$g->getPublicName(). 
-	" (".date('m/d/Y',$report->getStartDate()) ."-". date('m/d/Y',$report->getEndDate()) .")");
+	" (".date('Y-m-d',$report->getStartDate()) ." to ". date('Y-m-d',$report->getEndDate()) .")");
 $graph->subtitle->Set($report_company_name);
 //$graph->xaxis-> title->Set("Date" );
 //$graph->yaxis-> title->Set("Number" ); 

Modified: trunk/gforge/www/tracker/taskmgr.php
===================================================================
--- trunk/gforge/www/tracker/taskmgr.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/taskmgr.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -82,14 +82,14 @@
 
 	echo '
 		<h3>'._('Build Relationship Between Tracker Items and Task Manager').'</h3>
-		<p>
-		<form name="foo" action="'. getStringFromServer('PHP_SELF') .'?func=taskmgr&group_id='.$group_id.'&atid='.$atid.'&aid='.$aid.'" method="post">
-		<strong>'._('Tracker Item').':</strong> [#'.$a->getID().'] '.$a->getSummary().'<p>
-		<strong>'._('Task Manager Project').':</strong><br />';
+
+		<form name="foo" action="'. getStringFromServer('PHP_SELF') .'?func=taskmgr&amp;group_id='.$group_id.'&amp;atid='.$atid.'&amp;aid='.$aid.'" method="post">
+		<p><strong>'._('Tracker Item').':</strong> [#'.$a->getID().'] '.$a->getSummary().'</p>
+		<p><strong>'._('Task Manager Project').':</strong><br />';
 	echo $pg->getName().'
-		<input type="hidden" name="group_project_id" value="'.$pg->getID().'">
+		<input type="hidden" name="group_project_id" value="'.$pg->getID().'"></p>
 		<p>
-		<strong>'._('Task').':</strong><br />
+		<strong>'._('Task').':</strong></p>
 		<select name="project_task_id">';
 	for ($i=0; $i<count($pt_arr); $i++) {
 		echo '<option value="'.$pt_arr[$i]->getID().'">'.$pt_arr[$i]->getSummary().'</option>';
@@ -136,16 +136,16 @@
 	$ath->header(array('atid'=>$ath->getID()));
 
 	echo '<h3>'._('Build Relationship Between Tracker Items and Task Manager').'</h3>
-		<p><form name="foo" action="'. getStringFromServer('PHP_SELF') .'?func=taskmgr&group_id='.$group_id.'&atid='.$atid.'&aid='.$aid.'" method="post">
-		<strong>'._('Tracker Item').':</strong> [#'.$a->getID().'] '.$a->getSummary().'<p>
-		<strong>'._('Task Manager Project').':</strong><br />
+		<form name="foo" action="'. getStringFromServer('PHP_SELF') .'?func=taskmgr&amp;group_id='.$group_id.'&amp;atid='.$atid.'&amp;aid='.$aid.'" method="post">
+		<p><strong>'._('Tracker Item').':</strong> [#'.$a->getID().'] '.$a->getSummary().'</p>
+		<p><strong>'._('Task Manager Project').':</strong></p>
 		<select name="group_project_id">';
 	for ($i=0; $i<count($pg_arr); $i++) {
 		echo '<option value="'.$pg_arr[$i]->getID().'">'.$pg_arr[$i]->getName().'</option>';
 	}
 	echo '</select><br />
-		<input type="submit" name="add_to_task" value="'._('Add Relation To Existing Task.').'."><br />
-		<input type="submit" name="new_task" value="'._('Create New Task').'">
+		<input type="submit" name="add_to_task" value="'._('Add Relation To Existing Task.').'." /><br />
+		<input type="submit" name="new_task" value="'._('Create New Task').'" />
 		</form>';
 
 }

Modified: trunk/gforge/www/tracker/tracker.php
===================================================================
--- trunk/gforge/www/tracker/tracker.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/gforge/www/tracker/tracker.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -34,7 +34,7 @@
 
 	case 'add' : {
 		if (!$ath->allowsAnon() && !session_loggedin()) {
-			exit_error('ERROR',_('Artifact: This ArtifactType Does Not Allow Anonymous Submissions. Please Login.'));
+			exit_permission_denied();
 		} else {
 			include $gfwww.'tracker/add.php';
 		}
@@ -57,7 +57,7 @@
 		/*
 			Create a new Artifact
 
-		*/
+			*/
 		$ah=new ArtifactHtml($ath);
 		$feedback = '';
 		if (!$ah || !is_object($ah)) {
@@ -67,13 +67,17 @@
 			exit_error('ERROR',_('Artifact: This ArtifactType Does Not Allow Anonymous Submissions. Please Login.'));
 		} else {
 			if (empty($user_email)) {
-					$user_email=false;
+				$user_email=false;
 			} else {
 				if (!validate_email($user_email)) {
 					form_release_key(getStringFromRequest('form_key'));
 					exit_error('ERROR', _('Invalid Email Address'));
 				}
 			}
+			if ($user_email) {
+				$details = "Anonymous message posted by $user_email\n\n".
+				$details;
+			}
 			if (!$ah->create($summary,$details,$assigned_to,$priority,$extra_fields)) {
 				form_release_key(getStringFromRequest('form_key'));
 				exit_error('ERROR',$ah->getErrorMessage());
@@ -81,9 +85,18 @@
 				//
 				//	  Attach files to this Artifact.
 				//
+				$ext_feedback = '';
 				for ($i=0; $i<5; $i++) {
 					$error=$_FILES['input_file']['error'][$i];
 					if (isset($error) && $error > 0) {
+						$n = $i+1;
+						if ($error === 1 || $error === 2) {
+							// UPLOAD_ERR_INI_SIZE or UPLOAD_ERR_FORM_SIZE
+							$ext_feedback .= "<br />ERROR: Skipping attachement $n: file is too large.";
+						} elseif ($error === 3) {
+							// UPLOAD_ERR_PARTIAL
+							$ext_feedback .= "<br />ERROR: Skipping attachement $n: transfert interrupted.";
+						}
 						continue;
 					}
 					$file_name=$_FILES['input_file']['name'][$i];
@@ -97,8 +110,8 @@
 					$afh=new ArtifactFileHtml($ah);
 					if (!$afh || !is_object($afh)) {
 						$feedback .= 'Could Not Create File Object';
-  //				} elseif ($afh->isError()) {
-  //					$feedback .= $afh->getErrorMessage();
+						//				} elseif ($afh->isError()) {
+						//					$feedback .= $afh->getErrorMessage();
 					} else {
 						if (!util_check_fileupload($tmp_name)) {
 							form_release_key(getStringFromRequest('form_key'));
@@ -115,6 +128,7 @@
 					}
 				}
 				$feedback .= _('Item Successfully Created');
+				$feedback .= $ext_feedback;
 				include $gfwww.'tracker/browse.php';
 			}
 		}
@@ -134,7 +148,7 @@
 		$assigned_to = getStringFromRequest('assigned_to');
 		$canned_response = getIntFromRequest("canned_response");
 		$extra_fields = getArrayFromRequest('extra_fields');
-		
+
 		$count=count($artifact_id_list);
 
 		if (!$ath->userIsAdmin()) {
@@ -179,12 +193,14 @@
 						// in some cases (ie: textfields) the value is not passed, but
 						// this doesn't mean we must delete the existing value
 						if (array_key_exists($efid, $extra_fields)) {
-							$f = $extra_fields[$efid]; 	
+							$f = $extra_fields[$efid];
 							if ($f == '100') {
 								// no change
 							} else {
 								$ef[$efid] = $f;
 							}
+						} else {
+							$ef[$efid] = addslashes($ef[$efid]);
 						}
 					}
 				}
@@ -201,8 +217,8 @@
 			}
 			unset($ah);
 
-		if (!$was_error) {
-			$feedback = _('Updated successfully');			}
+			if (!$was_error) {
+				$feedback = _('Updated successfully');			}
 		}
 		unset ($extra_fields_choice);
 		include $gfwww.'tracker/browse.php';
@@ -219,18 +235,19 @@
 		$summary = getStringFromRequest('summary');
 		$canned_response = getStringFromRequest('canned_response');
 		$details = getStringFromRequest('details');
+		$description = getStringFromRequest('description');
 		$new_artifact_type_id = getIntFromRequest('new_artifact_type_id');
 		$extra_fields = getStringFromRequest('extra_fields');
 		$was_error = false;
-		
+
 		/*
 			Technicians can modify limited fields - to be certain
 			no one is hacking around, we override any fields they don't have
 			permission to change.
-		*/
+			*/
 		if (!form_key_is_valid(getStringFromRequest('form_key'))) {
 			exit_form_double_submit();
-		}	
+		}
 
 		$ah=new ArtifactHtml($ath,$artifact_id);
 		if (!$ah || !is_object($ah)) {
@@ -243,8 +260,8 @@
 
 			/*
 
-				The following logic causes fields to be overridden
-				in the event that someone tampered with the HTML form
+			The following logic causes fields to be overridden
+			in the event that someone tampered with the HTML form
 
 			*/
 			if ($ath->userIsAdmin() || $ath->userIsTechnician()) {
@@ -252,20 +269,17 @@
 				//admin and techs can do everything
 				//techs will have certain fields overridden inside the update() function call
 				if (!$ah->update($priority,$status_id,
-					$assigned_to,$summary,$canned_response,$details,$new_artifact_type_id,$extra_fields)) {
+				$assigned_to,$summary,$canned_response,$details,$new_artifact_type_id,$extra_fields, $description)) {
 					form_release_key(getStringFromRequest('form_key'));
-					$feedback =_('Tracker Item'). ': '.$ah->getErrorMessage();
+					$error_msg .= _('Tracker Item'). ': '.$ah->getErrorMessage();
 					$ah->clearError();
 					$was_error=true;
 				}
 
 			} else {
 
-				if (session_loggedin() && ($ah->getSubmittedBy() == user_getid())) {
-
-					//submitter can only add files & comments
-
-					$delete_file=false;
+				// Everyone alse can add comments
+				if ($details) {
 					if ($ah->addMessage($details,$user_email,true)) {
 						$feedback=_('Comment added');
 					} else {
@@ -291,214 +305,257 @@
 						//some kind of error in creation
 						exit_error('ERROR',$ah->getErrorMessage());
 					}
-
 				}
 			}
 
-			//
-			//	  Attach files to this Artifact.
-			//
-			for ($i=0; $i<5; $i++) {
-				$error=$_FILES['input_file']['error'][$i];
-				if (isset($error) && $error > 0) {
-					continue;
-				}
-				$file_name=$_FILES['input_file']['name'][$i];
-				$tmp_name=$_FILES['input_file']['tmp_name'][$i];
-				if (!is_uploaded_file($tmp_name)) {
-					continue;
-				}
-				$size=$_FILES['input_file']['size'][$i];
-				$type=$_FILES['input_file']['type'][$i];
-
-				$afh=new ArtifactFileHtml($ah);
-				if (!$afh || !is_object($afh)) {
-					$feedback .= 'Could Not Create File Object';
-  //			} elseif ($afh->isError()) {
-  //				$feedback .= $afh->getErrorMessage();
-				} else {
-					if (!util_check_fileupload($tmp_name)) {
-						form_release_key(getStringFromRequest('form_key'));
-						exit_error("Error","Invalid filename");
+			// Admin, Techs and Submitter can add files.
+			if ($ath->userIsAdmin() || $ath->userIsTechnician() ||
+			(session_loggedin() && ($ah->getSubmittedBy() == user_getid()))) {
+				//
+				//	  Attach files to this Artifact.
+				//
+				$ext_feedback = '';
+				for ($i=0; $i<5; $i++) {
+					$error=$_FILES['input_file']['error'][$i];
+					if (isset($error) && $error > 0) {
+						$n = $i+1;
+						if ($error === 1 || $error === 2) {
+							// UPLOAD_ERR_INI_SIZE or UPLOAD_ERR_FORM_SIZE
+							$ext_feedback .= "<br />ERROR: Skipping attachement $n: file is too large.";
+						} elseif ($error === 3) {
+							// UPLOAD_ERR_PARTIAL
+							$ext_feedback .= "<br />ERROR: Skipping attachement $n: transfert interrupted.";
+						}
+						continue;
 					}
-					if (!$afh->upload($tmp_name,$file_name,$type,' ')) {
-						$feedback .= ' <br />'._('File Upload: Error').':'.$afh->getErrorMessage();
-						$was_error=true;
-					} else {
-						$feedback .= ' <br />'._('File Upload: Successful');
-					}
-				}
-			}
+					$file_name=$_FILES['input_file']['name'][$i];
+					$tmp_name=$_FILES['input_file']['tmp_name'][$i];
+					if (!is_uploaded_file($tmp_name)) {
+						continue;
 
-			//
-			//	Delete list of files from this artifact
-			//
-			$delete_file = getStringFromRequest('delete_file');
-			if ($delete_file) {
-				$count=count($delete_file);
-				for ($i=0; $i<$count; $i++) {
-					$afh=new ArtifactFileHtml($ah,$delete_file[$i]);
-					if (!$afh || !is_object($afh)) {
-						$feedback .= 'Could Not Create File Object::'.$delete_file[$i];
-					} elseif ($afh->isError()) {
-						$feedback .= $afh->getErrorMessage().'::'.$delete_file[$i];
-					} else {
-						if (!$afh->delete()) {
-							$feedback .= ' <br />'._('File Delete:').': '.$afh->getErrorMessage();
+						if (!$afh->upload($tmp_name,$file_name,$type,' ')) {
+							$feedback .= ' <br />'._('File Upload: Error').':'.$afh->getErrorMessage();
 							$was_error=true;
 						} else {
-							$feedback .= ' <br />'._('File Delete: Successful');
+							$feedback .= ' <br />'._('File Upload: Successful');
 						}
+						$size=$_FILES['input_file']['size'][$i];
+						$type=$_FILES['input_file']['type'][$i];
+
+						$afh=new ArtifactFileHtml($ah);
+						if (!$afh || !is_object($afh)) {
+							$feedback .= 'Could Not Create File Object';
+							//			} elseif ($afh->isError()) {
+							//				$feedback .= $afh->getErrorMessage();
+						} else {
+							if (!util_check_fileupload($tmp_name)) {
+								form_release_key(getStringFromRequest('form_key'));
+								exit_error("Error","Invalid filename");
+							}
+							if (!$afh->upload($tmp_name,$file_name,$type,' ')) {
+								$feedback .= ' <br />'.$Language->getText('tracker','file_upload_upload').':'.$afh->getErrorMessage();
+								$was_error=true;
+							} else {
+								$feedback .= ' <br />'.$Language->getText('tracker','file_upload_successful');
+							}
+						}
 					}
 				}
+					
+				// Admin and Techs can delete files.
+				if ($ath->userIsAdmin() || $ath->userIsTechnician()) {
+					//
+					//	Delete list of files from this artifact
+					//
+					$delete_file = getStringFromRequest('delete_file');
+					if ($delete_file) {
+						$count=count($delete_file);
+						for ($i=0; $i<$count; $i++) {
+							$afh=new ArtifactFileHtml($ah,$delete_file[$i]);
+							if (!$afh || !is_object($afh)) {
+								$feedback .= 'Could Not Create File Object::'.$delete_file[$i];
+							} elseif ($afh->isError()) {
+								$feedback .= $afh->getErrorMessage().'::'.$delete_file[$i];
+							} else {
+								if (!$afh->delete()) {
+									$feedback .= ' <br />'._('File Delete:').': '.$afh->getErrorMessage();
+									$was_error=true;
+								} else {
+									$feedback .= ' <br />'._('File Delete: Successful');
+								}
+							}
+						}
+					}
+					//
+					//	Show just one feedback entry if no errors
+					//
+					if (!$was_error) {
+						$feedback = _('Updated successfully');
+					}
+					$feedback .= $ext_feedback;
+					include $gfwww.'tracker/browse.php';
+				}
+				break;
 			}
-			//
-			//	Show just one feedback entry if no errors
-			//
-			if (!$was_error) {
-				$feedback = _('Updated successfully');
-			}
-			include $gfwww.'tracker/browse.php';
 		}
-		break;
 	}
-	case 'monitor' : {
-		$artifact_id = getIntFromRequest('artifact_id');
-		if ($artifact_id) {
-			$ah=new ArtifactHtml($ath,$artifact_id);
-			if (!$ah || !is_object($ah)) {
-				exit_error('ERROR','Artifact Could Not Be Created');
-			} else if ($ah->isError()) {				
-				exit_error('ERROR',$ah->getErrorMessage());
+		case 'monitor' : {
+			if (!session_loggedin()) {
+				exit_permission_denied();
+			}
+			$start = getIntFromRequest('start');
+			$stop = getIntFromRequest('stop');
+			$artifact_id = getIntFromRequest('artifact_id');
+
+			// Fix to prevent collision with the start variable used in browse.
+			$_GET['start'] = 0;
+
+			if ($artifact_id) {
+				$ah=new ArtifactHtml($ath,$artifact_id);
+				if (!$ah || !is_object($ah)) {
+					exit_error('ERROR','Artifact Could Not Be Created');
+				} else if ($ah->isError()) {
+					exit_error('ERROR',$ah->getErrorMessage());
+				} else {
+					if ($start && $ah->isMonitoring())
+					$feedback = $Language->getText('tracker_monitor','monitoring_activated');
+					elseif ($stop && !$ah->isMonitoring())
+					$feedback = $Language->getText('tracker_monitor','monitoring_deactivated');
+					else {
+						$ah->setMonitor();
+						$feedback=$ah->getErrorMessage();
+					}
+					include $gfwww.'tracker/browse.php';
+				}
 			} else {
-				$ah->setMonitor();
-				$feedback=$ah->getErrorMessage();
+				$at=new ArtifactType($group,$atid);
+				if (!$at || !is_object($at)) {
+					exit_error('ERROR','Artifact Could Not Be Created');
+				} else if ($at->isError()) {
+					exit_error('ERROR',$at->getErrorMessage());
+				} else {
+					if ($start && $at->isMonitoring())
+					$feedback = $Language->getText('tracker_monitor','monitoring_activated');
+					elseif ($stop && !$at->isMonitoring())
+					$feedback = $Language->getText('tracker_monitor','monitoring_deactivated');
+					else {
+						$at->setMonitor();
+						$feedback=$at->getErrorMessage();
+						$at->clearError();
+					}
+					include $gfwww.'tracker/browse.php';
+				}
+			}
+			break;
+		}
 
-				include $gfwww.'tracker/browse.php';
-			}
-		} else {
-			$at=new ArtifactType($group,$atid);
-			if (!$at || !is_object($at)) {
-				exit_error('ERROR','Artifact Could Not Be Created');
-			} else if ($at->isError()) {				
-				exit_error('ERROR',$at->getErrorMessage());
+
+		//
+		//	Show delete form
+		//
+		case 'deleteartifact' : {
+			if ($ath->userIsAdmin()) {
+				$aid = getStringFromRequest('aid');
+				$ah= new ArtifactHtml($ath,$aid);
+				if (!$ah || !is_object($ah)) {
+					exit_error('ERROR','Artifact Could Not Be Created');
+				} elseif ($ah->isError()) {
+					exit_error('ERROR',$ah->getErrorMessage());
+				}
+				include $gfwww.'tracker/deleteartifact.php';
 			} else {
-				$at->setMonitor();
-				$feedback=$at->getErrorMessage();	
-				$at->clearError();
-				include $gfwww.'tracker/browse.php';
+				exit_permission_denied();
 			}
+			break;
 		}
-		break;
-	}
 
+		//
+		//	Handle the actual delete
+		//
 
-	//
-	//	Show delete form
-	//
-	case 'deleteartifact' : {
-		if ($ath->userIsAdmin()) {
-			$aid = getStringFromRequest('aid');
-			$ah= new ArtifactHtml($ath,$aid);
-			if (!$ah || !is_object($ah)) {
-				exit_error('ERROR','Artifact Could Not Be Created');
-			} elseif ($ah->isError()) {
-				exit_error('ERROR',$ah->getErrorMessage());
+		case 'postdeleteartifact' : {
+			if (!form_key_is_valid(getStringFromRequest('form_key'))) {
+				exit_form_double_submit();
 			}
-			include $gfwww.'tracker/deleteartifact.php';
-		} else {
-			exit_permission_denied();
+			if ($ath->userIsAdmin()) {
+				$aid = getStringFromRequest('aid');
+				$ah= new ArtifactHtml($ath,$aid);
+				if (!$ah || !is_object($ah)) {
+					exit_error('ERROR','Artifact Could Not Be Created');
+				} elseif ($ah->isError()) {
+					exit_error('ERROR',$ah->getErrorMessage());
+				}
+				if (!getStringFromRequest('confirm_delete')) {
+					$feedback .= _('Confirmation failed. Artifact not deleted');
+				}
+				else {
+					if (!$ah->delete(true)) {
+						$feedback .= _('Artifact Delete Failed') . ': '.$ah->getErrorMessage();
+					} else {
+						$feedback .= _('Artifact Deleted Successfully');
+					}
+				}
+				include $gfwww.'tracker/browse.php';
+			} else {
+				exit_permission_denied();
+			}
+			break;
 		}
-		break;
-	}
 
-	//
-	//	Handle the actual delete
-	//
 
-	case 'postdeleteartifact' : {
-		if (!form_key_is_valid(getStringFromRequest('form_key'))) {
-			exit_form_double_submit();
+		case 'taskmgr' : {
+			include $gfwww.'tracker/taskmgr.php';
+			break;
 		}
-		if ($ath->userIsAdmin()) {
+		case 'browse' : {
+			include $gfwww.'tracker/browse.php';
+			break;
+		}
+		case 'query' : {
+			include $gfwww.'tracker/query.php';
+			break;
+		}
+		case 'downloadcsv' : {
+			include $gfwww.'tracker/downloadcsv.php';
+			break;
+		}
+		case 'download' : {
 			$aid = getStringFromRequest('aid');
-			$ah= new ArtifactHtml($ath,$aid);
+			Header("Redirect: ".util_make_url ("/tracker/download.php?group_id=$group_id&atid=$atid&aid=$aid&file_id=$file_id"));
+			break;
+		}
+		case 'detail' : {
+			$aid = getStringFromRequest('aid');
+
+			//
+			//	users can modify their own tickets in a limited way if they submitted them
+			//	even if they are not artifact admins
+			//
+			$ah=new ArtifactHtml($ath,$aid);
 			if (!$ah || !is_object($ah)) {
 				exit_error('ERROR','Artifact Could Not Be Created');
-			} elseif ($ah->isError()) {
+			} else if ($ah->isError()) {
 				exit_error('ERROR',$ah->getErrorMessage());
-			}
-			if (!getStringFromRequest('confirm_delete')) {
-				$feedback .= _('Confirmation failed. Artifact not deleted');
-			}
-			else {
-				if (!$ah->delete(true)) {
-					$feedback .= _('Artifact Delete Failed') . ': '.$ah->getErrorMessage();
+			} else {
+				if ($ath->userIsAdmin()) {
+					include $gfwww.'tracker/mod.php';
+				} elseif ($ath->userIsTechnician()) {
+					include $gfwww.'tracker/mod-limited.php';
 				} else {
-					$feedback .= _('Artifact Deleted Successfully');
+					include $gfwww.'tracker/detail.php';
 				}
 			}
+			break;
+		}
+		default : {
 			include $gfwww.'tracker/browse.php';
-		} else {
-			exit_permission_denied();
+			break;
 		}
-		break;
 	}
 
+	// Local Variables:
+	// mode: php
+	// c-file-style: "bsd"
+	// End:
 
-	case 'taskmgr' : {
-		include $gfwww.'tracker/taskmgr.php';
-		break;
-	}
-	case 'browse' : {
-		include $gfwww.'tracker/browse.php';
-		break;
-	}
-	case 'query' : {
-		include $gfwww.'tracker/query.php';
-		break;
-	}
-	case 'downloadcsv' : {
-		include $gfwww.'tracker/downloadcsv.php';
-		break;
-	}
-	case 'download' : {
-		$aid = getStringFromRequest('aid');
-		Header("Redirect: ".util_make_url ("/tracker/download.php?group_id=$group_id&atid=$atid&aid=$aid&file_id=$file_id"));
-		break;
-	}
-	case 'detail' : {
-		$aid = getStringFromRequest('aid');
-
-		//
-		//	users can modify their own tickets in a limited way if they submitted them
-		//	even if they are not artifact admins
-		//
-		$ah=new ArtifactHtml($ath,$aid);
-		if (!$ah || !is_object($ah)) {
-			exit_error('ERROR','Artifact Could Not Be Created');
-		} else if ($ah->isError()) {
-			exit_error('ERROR',$ah->getErrorMessage());
-		} else {
-			if ($ath->userIsAdmin()) {
-				include $gfwww.'tracker/mod.php';
-			} elseif ($ath->userIsTechnician()) {
-				include $gfwww.'tracker/mod-limited.php';
-			} else {
-				include $gfwww.'tracker/detail.php';
-			}
-		}
-		break;
-	}
-	default : {
-		include $gfwww.'tracker/browse.php';
-		break;
-	}
-}
-
-// Local Variables:
-// mode: php
-// c-file-style: "bsd"
-// End:
-
-?>
+	?>

Modified: trunk/tests/TarCentos52Tests.php
===================================================================
--- trunk/tests/TarCentos52Tests.php	2009-05-18 16:53:54 UTC (rev 7686)
+++ trunk/tests/TarCentos52Tests.php	2009-05-18 21:33:45 UTC (rev 7687)
@@ -9,7 +9,7 @@
 
 // Selenium based tests
 require_once 'func/Site/AllTests.php';
-//require_once 'Trackers/AllTests.php';
+require_once 'func/Trackers/AllTests.php';
 require_once 'func/Tasks/AllTests.php';
 require_once 'func/Forums/AllTests.php';
 //require_once 'PluginsWiki/AllTests.php';
@@ -18,7 +18,7 @@
 //require_once 'scm/AllTests.php';
 //require_once 'docs/AllTests.php';
 
-class AllTests
+class TarCentos52Tests
 {
 	public static function main()
 	{
@@ -40,7 +40,7 @@
 
 		// Integration tests (Selenium).
 		$suite->addTest(Site_AllTests::suite());
-//		$suite->addTest(Trackers_AllTests::suite());
+		$suite->addTest(Trackers_AllTests::suite());
 		$suite->addTest(Tasks_AllTests::suite());
 		$suite->addTest(Forums_AllTests::suite());
 		$suite->addTest(News_AllTests::suite());




More information about the Fusionforge-commits mailing list