瀏覽代碼

New implementation for displaying long lists: pagination

git-svn-id: http://svn.code.sf.net/p/itop/code/trunk@1297 a333f486-631f-4898-b8df-5754b55c2be0
dflaven 14 年之前
父節點
當前提交
88049b657b

+ 89 - 59
application/cmdbabstract.class.inc.php

@@ -560,8 +560,15 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 		$sClassName = $oSet->GetFilter()->GetClass();
 		$aAttribs = array();
 		$sZListName = isset($aExtraParams['zlist']) ? ($aExtraParams['zlist']) : 'list';
-		$aList = self::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
-		$aList = array_merge($aList, $aExtraFields);
+		if ($sZListName !== false)
+		{
+			$aList = self::FlattenZList(MetaModel::GetZListItems($sClassName, $sZListName));
+			$aList = array_merge($aList, $aExtraFields);
+		}
+		else
+		{
+			$aList = $aExtraFields;
+		}
 
 		// Filter the list to removed linked set since we are not able to display them here
 		foreach($aList as $index => $sAttCode)
@@ -616,7 +623,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 		{
 			if (!$bSingleSelectMode)
 			{
-				$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$iListId}:not(:disabled)', this.checked);\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
+				$aAttribs['form::select'] = array('label' => "<input type=\"checkbox\" onClick=\"CheckAll('.selectList{$iListId}:not(:disabled)', this.checked);\" class=\"checkAll\"></input>", 'description' => Dict::S('UI:SelectAllToggle+'));
 			}
 			else
 			{
@@ -634,14 +641,14 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 		$aValues = array();
 		$bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true;
 		$iMaxObjects = -1;
-		if ($bDisplayLimit && $bTruncated)
-		{
+		//if ($bDisplayLimit && $bTruncated)
+		//{
 			if ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())
 			{
 				$iMaxObjects = MetaModel::GetConfig()->GetMinDisplayLimit();
 				$oSet->SetLimit($iMaxObjects);
 			}
-		}
+		//}
 		$oSet->Seek(0);
 		while (($oObj = $oSet->Fetch()) && ($iMaxObjects != 0))
 		{
@@ -699,58 +706,7 @@ abstract class cmdbAbstractObject extends CMDBObject implements iDisplay
 		{
 			$aExtraParams['query_params'][$sName] = $sValue;
 		}
-		if ($bDisplayLimit && $bTruncated && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit()))
-		{
-			// list truncated
-			$aExtraParams['display_limit'] = true;
-			$sHtml .= '<tr class="containerHeader"><td><span id="lbl_'.$iListId.'">'.$sCollapsedLabel.'</span>&nbsp;&nbsp;<a class="truncated" id="trc_'.$iListId.'">'.$sLinkLabel.'</a></td><td>';
-			$oPage->add_ready_script(
-<<<EOF
-	$('#$iListId table.listResults').addClass('truncated');
-	$('#$iListId table.listResults tr:last td').addClass('truncated');
-EOF
-);
-		}
-		else if ($bDisplayLimit && !$bTruncated && ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit()))
-		{
-			// Collapsible list
-			$aExtraParams['display_limit'] = true;
-			$sHtml .= '<tr class="containerHeader"><td><span id="lbl_'.$iListId.'">'.Dict::Format('UI:CountOfResults', $oSet->Count()).'</span><a class="truncated" id="trc_'.$iListId.'">'.Dict::S('UI:CollapseList').'</a></td><td>';
-		}
-		$aExtraParams['truncated'] = false; // To expand the full list when clicked
-		$sExtraParamsExpand = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
-		$oPage->add_ready_script(
-<<<EOF
-	// Handle truncated lists
-	$('#trc_$iListId').click(function()
-	{
-		var state = {};
-		
-		var currentState = $.bbq.getState( this.id, true ) || 'close';	  
-		// Toggle the state!
-		if (currentState == 'close')
-		{
-			state[ this.id ] = 'open';
-		}
-		else
-		{
-			state[ this.id ] = 'close';			
-		}
-		$.bbq.pushState( state );
-		$(this).trigger(state[this.id]);	
-	});
-	$('#trc_$iListId').unbind('open');
-	$('#trc_$iListId').bind('open', function()
-	{
-		ReloadTruncatedList('$iListId', '$sFilter', '$sExtraParamsExpand');
-	});
-	$('#trc_$iListId').unbind('close');	
-	$('#trc_$iListId').bind('close', function()
-	{
-		TruncateList('$iListId', $iMinDisplayLimit, '$sCollapsedLabel', '$sLinkLabel');
-	});
-EOF
-);
+
 		if ($bDisplayMenu)
 		{
 			$oMenuBlock = new MenuBlock($oSet->GetFilter());
@@ -768,6 +724,80 @@ EOF
 		$sHtml .= $oPage->GetTable($aAttribs, $aValues);
 		$sHtml .= '</td></tr>';
 		$sHtml .= '</table>';
+		if ($oSet->Count() > MetaModel::GetConfig()->GetMaxDisplayLimit())
+		{
+			$iCount = $oSet->Count();
+$sHtml =
+<<<EOF
+<div id="pager{$iListId}" class="pager">
+		</p><span id="total">0</span> items.&nbsp;&nbsp;<span class="selectedCount"></span> item(s) selected.</p>
+		<p><table class="pagination"><tr><td>Pages:</td><td><img src="../images/first.png" class="first"/></td>
+		<td><img src="../images/prev.png" class="prev"/></td>
+		<td><span id="index"></span></td>
+		<td><img src="../images/next.png" class="next"/></td>
+		<td><img src="../images/last.png" class="last"/></td>
+		<td><select class="pagesize">
+			<option selected="selected" value="10">10</option>
+			<option value="20">20</option>
+			<option value="30">30</option>
+			<option  value="40">40</option>
+		</select>
+		items per page.</td>
+		<td><span id="loading">&nbsp;</span></td>
+		</tr>
+		</table>
+		
+		<input type="hidden" name="selectionMode" value="positive"></input>
+</div>
+EOF
+.$sHtml;
+			//$oP->add_ready_script("table.tablesorter( { headers: { 0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager')});\n");
+			$sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them
+			$sSelectMode = '';
+			$sHeaders = '';
+			if ($bSelectMode)
+			{
+				$sSelectMode = $bSingleSelectMode ? 'single' : 'multiple';
+				$sHeaders = 'headers: { 0: {sorter: false}},';
+			}
+			$sDisplayKey = ($bViewLink) ? 'true' : 'false';
+			$sDisplayList = json_encode($aList);
+			$sCssCount = isset($aExtraParams['cssCount']) ? ", cssCount: '{$aExtraParams['cssCount']}'" : '';
+			$oPage->add_ready_script("$('#{$iListId} table.listResults').tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} ).tablesorterPager({container: $('#pager{$iListId}'), totalRows:$iCount, filter: '$sFilter', extra_params: '$sExtraParams', select_mode: '$sSelectMode', displayKey: $sDisplayKey, displayList: $sDisplayList $sCssCount});\n");
+		}
+		else
+		{
+			$sHeaders = '';
+			if ($bSelectMode)
+			{
+				$sHeaders = 'headers: { 0: {sorter: false}},';
+			}
+			$oPage->add_ready_script("$('#{$iListId} table.listResults').tablesorter( { $sHeaders widgets: ['myZebra', 'truncatedList']} );\n");
+			// Manage how we update the 'Ok/Add' buttons that depend on the number of selected items
+			if (isset($aExtraParams['cssCount']))
+			{
+				$sCssCount = $aExtraParams['cssCount'];
+				if ($bSingleSelectMode)
+				{
+					$sSelectSelector = ":radio[name^=selectObj]";
+				}
+				else
+				{
+					$sSelectSelector = ":checkbox[name^=selectObj]";
+				}
+				$oPage->add_ready_script(
+<<<EOF
+	$('#{$iListId} table.listResults $sSelectSelector').change(function() {
+		var c = $('{$sCssCount}');							
+		var v = $('#{$iListId} table.listResults $sSelectSelector:checked').length;
+		c.val(v);
+		c.trigger('change');	
+	});
+EOF
+				);
+			}
+		}
+
 		return $sHtml;
 	}
 	
@@ -1898,7 +1928,7 @@ EOF
 		return $aDetails;
 	}
 
-	protected static function FlattenZList($aList)
+	static function FlattenZList($aList)
 	{
 		$aResult = array();
 		foreach($aList as $value)

+ 1 - 21
application/itopwebpage.class.inc.php

@@ -72,6 +72,7 @@ class iTopWebPage extends NiceWebPage
 		$this->add_linked_script("../js/ckeditor/ckeditor.js");
 		$this->add_linked_script("../js/ckeditor/adapters/jquery.js");
 		$this->add_linked_script("../js/jquery.qtip-1.0.min.js");
+		$this->add_linked_script("../js/jquery.tablesorter.pager.js");
 		$this->m_sInitScript =
 <<< EOF
 	try
@@ -289,23 +290,6 @@ EOF
 	  
 	// End of Tabs handling
 	$("table.listResults").tableHover(); // hover tables
-	// Check each 'listResults' table for a checkbox in the first column and make the first column sortable only if it does not contain a checkbox in the header
-	$(".listResults").each( function()
-		{
-			var table = $(this);
-			var id = $(this).parent();
-			var checkbox = (table.find('th:first :checkbox').length > 0);
-			if (checkbox)
-			{
-				// There is a checkbox in the first column, don't make it sortable
-				table.tablesorter( { headers: { 0: {sorter: false}}, widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
-			}
-			else
-			{
-				// There is NO checkbox in the first column, all columns are considered sortable
-				table.tablesorter( { widgets: ['myZebra', 'truncatedList']} ); // sortable and zebra tables
-			}
-		});
 	$(".date-pick").datepicker({
 			showOn: 'button',
 			buttonImage: '../images/calendar.png',
@@ -368,10 +352,6 @@ EOF
 //			}
 //		}
 
-		function formatItem(row) {
-			return row[0];
-		}
-		
 		function goBack()
 		{
 			window.history.back();

+ 3 - 2
application/ui.extkeywidget.class.inc.php

@@ -233,6 +233,7 @@ EOF
 		$sHTML .= "</div>\n";
 		$sHTML .= "<input type=\"button\" id=\"btn_cancel_{$this->iId}\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#ac_dlg_{$this->iId}').dialog('close');\">&nbsp;&nbsp;";
 		$sHTML .= "<input type=\"button\" id=\"btn_ok_{$this->iId}\" value=\"".Dict::S('UI:Button:Ok')."\"  onClick=\"oACWidget_{$this->iId}.DoOk();\">";
+		$sHTML .= "<input type=\"hidden\" id=\"count_{$this->iId}\" value=\"0\">";
 		$sHTML .= "</form>\n";
 		$sHTML .= '</div></div>';
 
@@ -263,7 +264,7 @@ EOF
 		{
 			$oFilter = DBObjectSearch::FromOQL($sFilter);
 			$oBlock = new DisplayBlock($oFilter, 'list', false);
-			$oBlock->Display($oP, $this->iId, array('this' => $oObj, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'display_limit' => false)); // Don't display the 'Actions' menu on the results
+			$oBlock->Display($oP, $this->iId.'_results', array('this' => $oObj, 'cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'display_limit' => false)); // Don't display the 'Actions' menu on the results
 		}
 		catch(MissingQueryArgument $e)
 		{
@@ -272,7 +273,7 @@ EOF
 			$sOQL = 'SELECT '.$sRemoteClass;
 			$oFilter = DBObjectSearch::FromOQL($sOQL);
 			$oBlock = new DisplayBlock($oFilter, 'list', false);
-			$oBlock->Display($oP, $this->iId, array('menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'display_limit' => false)); // Don't display the 'Actions' menu on the results
+			$oBlock->Display($oP, $this->iId.'_results', array('cssCount'=> '#count_'.$this->iId, 'menu' => false, 'selection_mode' => true, 'selection_type' => 'single', 'display_limit' => false)); // Don't display the 'Actions' menu on the results
 		}
 	}
 	

+ 16 - 4
application/ui.linkswidget.class.inc.php

@@ -270,7 +270,8 @@ EOF
 		$sHtml .= "<div id=\"SearchResultsToAdd_{$this->m_sAttCode}{$this->m_sNameSuffix}\" style=\"vertical-align:top;background: #fff;height:100%;overflow:auto;padding:0;border:0;\">\n";
 		$sHtml .= "<div style=\"background: #fff; border:0; text-align:center; vertical-align:middle;\"><p>".Dict::S('UI:Message:EmptyList:UseSearchForm')."</p></div>\n";
 		$sHtml .= "</div>\n";
-		$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
+		$sHtml .= "<input type=\"hidden\" id=\"count_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"0\"/>";
+		$sHtml .= "<input type=\"button\" value=\"".Dict::S('UI:Button:Cancel')."\" onClick=\"$('#dlg_{$this->m_sAttCode}{$this->m_sNameSuffix}').dialog('close');\">&nbsp;&nbsp;<input id=\"btn_ok_{$this->m_sAttCode}{$this->m_sNameSuffix}\" type=\"submit\" value=\"".Dict::S('UI:Button:Add')."\">";
 		$sHtml .= "</div>\n";
 		$sHtml .= "</form>\n";
 		$sHtml .= "</div>\n";
@@ -332,12 +333,23 @@ EOF
 		}
 		$oSet = new CMDBObjectSet($oFilter);
 		$oBlock = new DisplayBlock($oFilter, 'list', false);
-		$oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
+		$oBlock->Display($oP, "ResultsToAdd_{$this->m_sAttCode}", array('menu' => false, 'cssCount'=> '#count_'.$this->m_sAttCode.$this->m_sNameSuffix , 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results
 	}
 	
-	public function DoAddObjects(WebPage $oP, $aLinkedObjectIds = array())
+	public function DoAddObjects(WebPage $oP, $sRemoteClass)
 	{
-		$aTable = array();
+		if ($sRemoteClass != '')
+		{
+			// assert(MetaModel::IsParentClass($this->m_sRemoteClass, $sRemoteClass));
+			$oFullSetFilter = new DBObjectSearch($sRemoteClass);
+		}
+		else
+		{
+			// No remote class specified use the one defined in the linkedset
+			$oFullSetFilter = new DBObjectSearch($this->m_sRemoteClass);		
+		}
+		$aLinkedObjectIds = utils::ReadMultipleSelection($oFullSetFilter);
+
 		foreach($aLinkedObjectIds as $iObjectId)
 		{
 			$oLinkedObj = MetaModel::GetObject($this->m_sRemoteClass, $iObjectId);

+ 37 - 0
application/utils.inc.php

@@ -217,6 +217,43 @@ class utils
 		return $oDocument;
 	}
 	
+	/**
+	 * Interprets the results posted by a normal or paginated list (in multiple selection mode)
+	 * @param $oFullSetFilter DBObjectSearch The criteria defining the whole sets of objects being selected
+	 * @return Array An arry of object IDs corresponding to the objects selected in the set
+	 */	
+	public static function ReadMultipleSelection($oFullSetFilter)
+	{
+		$aSelectedObj = utils::ReadParam('selectObject', array());
+		$sSelectionMode = utils::ReadParam('selectionMode', '');
+		if ($sSelectionMode != '')
+		{
+			// Paginated selection
+			$aExceptions = utils::ReadParam('storedSelection', array());
+			if ($sSelectionMode == 'positive')
+			{
+				// Only the explicitely listed items are selected
+				$aSelectedObj = $aExceptions;
+			}
+			else
+			{
+				// All items of the set are selected, except the one explicitely listed
+				$aSelectedObj = array();
+				$oFullSet = new DBObjectSet($oFullSetFilter);
+				$sClassAlias = $oFullSetFilter->GetClassAlias();
+				$oFullSet->OptimizeColumnLoad(array($sClassAlias => array('friendlyname'))); // We really need only the IDs but it does not work since id is not a real field
+				while($oObj = $oFullSet->Fetch())
+				{
+					if (!in_array($oObj->GetKey(), $aExceptions))
+					{
+						$aSelectedObj[] = $oObj->GetKey();
+					}
+				}
+			}
+		}
+		return $aSelectedObj;
+	}
+
 	public static function GetNewTransactionId()
 	{
 		return privUITransaction::GetNewTransactionId();

+ 22 - 16
application/webpage.class.inc.php

@@ -154,27 +154,33 @@ class WebPage
 		$sHtml .= "<tbody>\n";
 		foreach($aData as $aRow)
 		{
-			if (isset($aRow['@class'])) // Row specific class, for hilighting certain rows
-			{
-				$sHtml .= "<tr class=\"{$aRow['@class']}\">\n";				
-			}
-			else
-			{
-				$sHtml .= "<tr>\n";
-			}
-			foreach($aConfig as $sName=>$aAttribs)
-			{
-				$aMatches = array();
-				$sClass = isset($aAttribs['class']) ? 'class="'.$aAttribs['class'].'"' : '';
-				$sValue = ($aRow[$sName] === '') ? '&nbsp;' : $aRow[$sName];
-				$sHtml .= "<td $sClass>$sValue</td>\n";
-			}
-			$sHtml .= "</tr>\n";
+			$sHtml .= $this->GetTableRow($aRow, $aConfig);
 		}
 		$sHtml .= "</tbody>\n";
 		$sHtml .= "</table>\n";
 		return $sHtml;
 	}
+	
+	public function GetTableRow($aRow, $aConfig)
+	{
+		$sHtml = '';
+		if (isset($aRow['@class'])) // Row specific class, for hilighting certain rows
+		{
+			$sHtml .= "<tr class=\"{$aRow['@class']}\">";				
+		}
+		else
+		{
+			$sHtml .= "<tr>";
+		}
+		foreach($aConfig as $sName=>$aAttribs)
+		{
+			$sClass = isset($aAttribs['class']) ? 'class="'.$aAttribs['class'].'"' : '';
+			$sValue = ($aRow[$sName] === '') ? '&nbsp;' : $aRow[$sName];
+			$sHtml .= "<td $sClass>$sValue</td>";
+		}
+		$sHtml .= "</tr>";
+		return $sHtml;	
+	}
     
 	/**
 	 * Add some Javascript to the header of the page