Merge branch 'master' of github.com:magicbug/Cloudlog
这个提交包含在:
		
						当前提交
						67ae43de13
					
				
					共有  6 个文件被更改,包括 164 次插入 和 69 次删除
				
			
		|  | @ -149,40 +149,51 @@ class API extends CI_Controller { | ||||||
| 		$this->load->model('api_model'); | 		$this->load->model('api_model'); | ||||||
| 		$this->load->model('logbook_model'); | 		$this->load->model('logbook_model'); | ||||||
| 		$this->load->model('user_model'); | 		$this->load->model('user_model'); | ||||||
| 		//if(!$this->user_model->authorize(3)) { $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); }
 | 
 | ||||||
|  | 		$arguments = $this->_retrieve(); | ||||||
|  | 
 | ||||||
|  | 		if((!$this->user_model->authorize(3)) && ($this->api_model->authorize($arguments['key']) == 0)) { | ||||||
|  |       $this->session->set_flashdata('notice', 'You\'re not allowed to do that!'); redirect('dashboard'); | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 		// Retrieve the arguments from the query string
 | 		// Retrieve the arguments from the query string
 | ||||||
| 		$arguments = $this->_retrieve(); |     $data['data']['format'] = $arguments['format']; | ||||||
| 	 | 	 | ||||||
| 		// Call the parser within the API model to build the query
 | 		// Call the parser within the API model to build the query
 | ||||||
| 		$query = $this->api_model->select_parse($arguments); | 		$query = $this->api_model->select_parse($arguments); | ||||||
| 
 | 
 | ||||||
| 		// Execute the query, and retrieve the results
 | 		// Execute the query, and retrieve the results
 | ||||||
| 		$s = $this->logbook_model->api_search_query($query); | 		$s = $this->logbook_model->api_search_query($query); | ||||||
| 		$results = $s['results']; |  | ||||||
| 
 |  | ||||||
| 		// Cycle through the results, and translate between MySQL column names
 |  | ||||||
| 		// and more friendly, descriptive names
 |  | ||||||
| 		$a = 0; | 		$a = 0; | ||||||
| 		if($results->num_rows != 0) | 
 | ||||||
| 		{ |     if(isset($s['results'])) { | ||||||
| 			foreach ($results->result() as $row) { |   		$results = $s['results']; | ||||||
| 				$record = (array)$row; | 
 | ||||||
| 				$r[$a]['rid'] = $a; | 	  	// Cycle through the results, and translate between MySQL column names
 | ||||||
| 				while (list($key, $val) = each($record)) { |   		// and more friendly, descriptive names
 | ||||||
| 					$r[$a][$this->api_model->name($key)] = $val; |   		if($results->num_rows != 0) | ||||||
| 				} |   		{ | ||||||
|        			$a++; |   			foreach ($results->result() as $row) { | ||||||
| 			} |   				$record = (array)$row; | ||||||
| 			// Add the result record to the main results array
 |   				$r[$a]['rid'] = $a; | ||||||
|  |   				while (list($key, $val) = each($record)) { | ||||||
|  |   					$r[$a][$this->api_model->name($key)] = $val; | ||||||
|  |   				} | ||||||
|  |     			$a++; | ||||||
|  |   			} | ||||||
|  |   			// Add the result record to the main results array
 | ||||||
| 	    	$data['data']['search_Result']['results'] = $r; | 	    	$data['data']['search_Result']['results'] = $r; | ||||||
| 		} |   		} | ||||||
| 		else |   		else | ||||||
| 		{ |   		{ | ||||||
| 			// We've got no results, so make this empty for completeness
 |   			// We've got no results, so make this empty for completeness
 | ||||||
| 		    $data['data']['search_Result']['results'] = ""; |   	    $data['data']['search_Result']['results'] = ""; | ||||||
| 		} |   		} | ||||||
| 		 |     } else { | ||||||
|  |       $data['data']['error'] = $s['error']; | ||||||
|  |       $data['data']['search_Result']['results'] = ""; | ||||||
|  |     } | ||||||
|  | 
 | ||||||
| 		// Add some debugging information to the XML output
 | 		// Add some debugging information to the XML output
 | ||||||
| 		$data['data']['queryInfo']['call'] = "search"; | 		$data['data']['queryInfo']['call'] = "search"; | ||||||
| 		$data['data']['queryInfo']['dbQuery'] = $s['query']; | 		$data['data']['queryInfo']['dbQuery'] = $s['query']; | ||||||
|  | @ -193,6 +204,27 @@ class API extends CI_Controller { | ||||||
| 		$this->load->view('api/index', $data); | 		$this->load->view('api/index', $data); | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |   function validate() | ||||||
|  |   { | ||||||
|  | 		// Load the API and Logbook models
 | ||||||
|  | 		$this->load->model('api_model'); | ||||||
|  | 		$this->load->model('logbook_model'); | ||||||
|  | 
 | ||||||
|  | 		// Retrieve the arguments from the query string
 | ||||||
|  | 		$arguments = $this->_retrieve(); | ||||||
|  | 
 | ||||||
|  | 		// Add some debugging information to the XML output
 | ||||||
|  |     $data['data'] = $arguments; | ||||||
|  | 		$data['data']['queryInfo']['call'] = "validate"; | ||||||
|  | 		$data['data']['queryInfo']['dbQuery'] = ""; | ||||||
|  | 		$data['data']['queryInfo']['numResults'] = 1; | ||||||
|  | 		$data['data']['queryInfo']['executionTime'] = 0; | ||||||
|  | 
 | ||||||
|  |     $data['data']['validate_Result']['results'] = array(0 => array('Result' => $this->api_model->authorize($arguments['key']))); | ||||||
|  | 
 | ||||||
|  |     $this->load->view('api/index', $data); | ||||||
|  |   } | ||||||
|  |      | ||||||
| 	function add() | 	function add() | ||||||
| 	{ | 	{ | ||||||
| 		// Load the API and Logbook models
 | 		// Load the API and Logbook models
 | ||||||
|  | @ -245,20 +277,31 @@ class API extends CI_Controller { | ||||||
| 		$arguments = array(); | 		$arguments = array(); | ||||||
| 
 | 
 | ||||||
| 		// Retrieve each arguments
 | 		// Retrieve each arguments
 | ||||||
| 		$query = preg_grep("/^query\[(.*)\]$/", $this->uri->segments); | 		$query = preg_grep("/^query=(.*)$/", $this->uri->segments); | ||||||
| 		$limit = preg_grep("/^limit\[(.*)\]$/", $this->uri->segments); | 		$limit = preg_grep("/^limit=(.*)$/", $this->uri->segments); | ||||||
| 		$order = preg_grep("/^order\[(.*)\]$/", $this->uri->segments); | 		$order = preg_grep("/^order=(.*)$/", $this->uri->segments); | ||||||
| 		$fields = preg_grep("/^fields\[(.*)\]$/", $this->uri->segments); | 		$fields = preg_grep("/^fields=(.*)$/", $this->uri->segments); | ||||||
|  | 		$format = preg_grep("/^format=(.*)$/", $this->uri->segments); | ||||||
|  | 		$key = preg_grep("/^key=(.*)$/", $this->uri->segments); | ||||||
| 
 | 
 | ||||||
| 		// Strip each argument
 | 		// Strip each argument
 | ||||||
| 		$arguments['query'] = substr(array_pop($query), 6); | 		$arguments['query'] = substr(array_pop($query), 6); | ||||||
| 		$arguments['query'] = substr($arguments['query'], 0, strlen($arguments['query']) - 1); | 		$arguments['query'] = substr($arguments['query'], 0, strlen($arguments['query'])); | ||||||
| 		$arguments['limit'] = substr(array_pop($limit), 6); | 		$arguments['limit'] = substr(array_pop($limit), 6); | ||||||
| 		$arguments['limit'] = substr($arguments['limit'], 0, strlen($arguments['limit']) - 1); | 		$arguments['limit'] = substr($arguments['limit'], 0, strlen($arguments['limit'])); | ||||||
| 		$arguments['order'] = substr(array_pop($order), 6); | 		$arguments['order'] = substr(array_pop($order), 6); | ||||||
| 		$arguments['order'] = substr($arguments['order'], 0, strlen($arguments['order']) - 1); | 		$arguments['order'] = substr($arguments['order'], 0, strlen($arguments['order'])); | ||||||
| 		$arguments['fields'] = substr(array_pop($fields), 7); | 		$arguments['fields'] = substr(array_pop($fields), 7); | ||||||
| 		$arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields']) - 1); | 		$arguments['fields'] = substr($arguments['fields'], 0, strlen($arguments['fields'])); | ||||||
|  | 		$arguments['format'] = substr(array_pop($format), 7); | ||||||
|  | 		$arguments['format'] = substr($arguments['format'], 0, strlen($arguments['format'])); | ||||||
|  | 		$arguments['key'] = substr(array_pop($key), 4); | ||||||
|  | 		$arguments['key'] = substr($arguments['key'], 0, strlen($arguments['key'])); | ||||||
|  | 
 | ||||||
|  |     // By default, assume XML for the format if not otherwise set
 | ||||||
|  |     if($arguments['format'] == "") { | ||||||
|  |       $arguments['format'] = "xml"; | ||||||
|  |     } | ||||||
| 
 | 
 | ||||||
| 		// Return the arguments
 | 		// Return the arguments
 | ||||||
| 		return $arguments; | 		return $arguments; | ||||||
|  |  | ||||||
|  | @ -38,26 +38,41 @@ class API_Model extends CI_Model { | ||||||
| 
 | 
 | ||||||
|     function access($key) { |     function access($key) { | ||||||
|     	 |     	 | ||||||
|  |       // No key = no access, mate
 | ||||||
|  |       if(!$key) { | ||||||
|  |         return $status = "No Key Found"; | ||||||
|  |       } | ||||||
|  | 
 | ||||||
|     	// Check that the key is valid
 |     	// Check that the key is valid
 | ||||||
|     	$this->db->where('key', $key);  |     	$this->db->where('key', $key);  | ||||||
|     	$query = $this->db->get('api'); |      	$query = $this->db->get('api'); | ||||||
| 
 | 
 | ||||||
| 		if ($query->num_rows() > 0) | 		  if ($query->num_rows() > 0) | ||||||
| 		{ |   		{ | ||||||
| 		   foreach ($query->result() as $row) |         foreach ($query->result() as $row) | ||||||
| 		   { | 	      { | ||||||
| 		   		 if($row->status == "active") { | 	     	  if($row->status == "active") { | ||||||
| 		   		 	return $status = $row->rights; | 	   	  	  return $status = $row->rights; | ||||||
| 		   		 } else { | 	   		  } else { | ||||||
| 		   		 	return $status = "Key Disabled"; |  		   		 	return $status = "Key Disabled"; | ||||||
| 		   		 } |   	   		} | ||||||
| 
 | 	      } | ||||||
| 		   } | 		  } else { | ||||||
| 		} else { | 			  return $status = "No Key Found"; | ||||||
| 			return $status = "No Key Found"; |   		} | ||||||
| 		} |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|  |   function authorize($key) { | ||||||
|  |     $r = $this->access($key); | ||||||
|  |     if($r == "rw") { | ||||||
|  |       return 2; | ||||||
|  |     } else if($r == "r") { | ||||||
|  |       return 1; | ||||||
|  |     } else { | ||||||
|  |       return 0; | ||||||
|  |     } | ||||||
|  |   } | ||||||
|  | 
 | ||||||
| 	// FUNCTION: string name(string $column)
 | 	// FUNCTION: string name(string $column)
 | ||||||
| 	// Converts a MySQL column name to a more friendly name
 | 	// Converts a MySQL column name to a more friendly name
 | ||||||
| 	function name($col) | 	function name($col) | ||||||
|  | @ -178,6 +193,8 @@ class API_Model extends CI_Model { | ||||||
| 		    $s[3]   = '/\(asc\)/'; | 		    $s[3]   = '/\(asc\)/'; | ||||||
| 	    	$s[4]   = '/\(desc\)/'; | 	    	$s[4]   = '/\(desc\)/'; | ||||||
| 			$s[5]	= '/,$/'; | 			$s[5]	= '/,$/'; | ||||||
|  |       $s[6] = '/\[/'; | ||||||
|  |       $s[7] = '/\]/'; | ||||||
| 
 | 
 | ||||||
| 			$r[0]	= '('; | 			$r[0]	= '('; | ||||||
| 			$r[1]	= ')'; | 			$r[1]	= ')'; | ||||||
|  | @ -185,6 +202,8 @@ class API_Model extends CI_Model { | ||||||
| 		    $r[3]   = ' ASC '; | 		    $r[3]   = ' ASC '; | ||||||
| 		    $r[4]   = ' DESC '; | 		    $r[4]   = ' DESC '; | ||||||
| 			$r[5]	= ''; | 			$r[5]	= ''; | ||||||
|  | 			$r[6]	= ''; | ||||||
|  | 			$r[7]	= ''; | ||||||
| 
 | 
 | ||||||
| 			$q .= preg_replace($s, $r, $arguments['order']); | 			$q .= preg_replace($s, $r, $arguments['order']); | ||||||
| 
 | 
 | ||||||
|  | @ -243,8 +262,9 @@ class API_Model extends CI_Model { | ||||||
| 			$r[1]   = ' OR '; | 			$r[1]   = ' OR '; | ||||||
| 			$r[2]   = ' < '; | 			$r[2]   = ' < '; | ||||||
| 			$r[3]   = ' > '; | 			$r[3]   = ' > '; | ||||||
| 			$r[4]   = '['; |       // Strip out square brackets
 | ||||||
| 			$r[5]   = ']'; | 			$r[4]   = ''; | ||||||
|  | 			$r[5]   = ''; | ||||||
| 			$r[6]   = '++$1++ ='; | 			$r[6]   = '++$1++ ='; | ||||||
| 			$r[7]   = '= \'$1\''; | 			$r[7]   = '= \'$1\''; | ||||||
| 			$r[8]   = 'UNIX_TIMESTAMP(NOW())'; | 			$r[8]   = 'UNIX_TIMESTAMP(NOW())'; | ||||||
|  |  | ||||||
|  | @ -411,7 +411,10 @@ class Logbook_model extends CI_Model { | ||||||
| 
 | 
 | ||||||
| 	function api_search_query($query) { | 	function api_search_query($query) { | ||||||
| 		$time_start = microtime(true); | 		$time_start = microtime(true); | ||||||
| 		$results = @$this->db->query($query); | 		$results = $this->db->query($query); | ||||||
|  |     if(!$results) { | ||||||
|  |       return array('query' => $query, 'error' => $this->db->_error_number(), 'time' => 0); | ||||||
|  |     } | ||||||
| 		$time_end = microtime(true); | 		$time_end = microtime(true); | ||||||
| 		$time = round($time_end - $time_start, 4); | 		$time = round($time_end - $time_start, 4); | ||||||
| 
 | 
 | ||||||
|  | @ -421,6 +424,9 @@ class Logbook_model extends CI_Model { | ||||||
| 	function api_insert_query($query) { | 	function api_insert_query($query) { | ||||||
| 		$time_start = microtime(true); | 		$time_start = microtime(true); | ||||||
| 		$results = $this->db->insert($this->config->item('table_name'), $query); | 		$results = $this->db->insert($this->config->item('table_name'), $query); | ||||||
|  |     if(!$results) { | ||||||
|  |       return array('query' => $query, 'error' => $this->db->_error_number(), 'time' => 0); | ||||||
|  |     } | ||||||
| 		$time_end = microtime(true); | 		$time_end = microtime(true); | ||||||
| 		$time = round($time_end - $time_start, 4); | 		$time = round($time_end - $time_start, 4); | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -64,7 +64,7 @@ | ||||||
| 				?>
 | 				?>
 | ||||||
| 
 | 
 | ||||||
| 			</td> | 			</td> | ||||||
| 			<td><?php echo ucfirst($row->status); ?></td>
 | 			<td><?php echo ucfirst($row->status); ?> - <a href="<?php echo site_url('/api/validate/key='.$row->key.']'); ?>">Test</td>
 | ||||||
| 		</tr> | 		</tr> | ||||||
| 
 | 
 | ||||||
| 	<?php } ?>
 | 	<?php } ?>
 | ||||||
|  | @ -81,15 +81,15 @@ | ||||||
| 		<li><a href="<?php echo site_url('api/generate/rw'); ?>">Key with Read & Write Access</a></li> | 		<li><a href="<?php echo site_url('api/generate/rw'); ?>">Key with Read & Write Access</a></li> | ||||||
| 		<li><a href="<?php echo site_url('api/generate/r'); ?>">Key with Read Only Access</a></li> | 		<li><a href="<?php echo site_url('api/generate/r'); ?>">Key with Read Only Access</a></li> | ||||||
| 	</ul> | 	</ul> | ||||||
| There are a number of API calls you can make from other applications. | There are a number of API calls you can make from other applications, with output available in either XML or JSON. | ||||||
| <h3>API Guide</h3> | <h3>API Guide</h3> | ||||||
| <h4>Description</h4> | <h4>Description</h4> | ||||||
| Query the logbook | Query the logbook, and output in XML format. | ||||||
| <h4>Syntax</h4> | <h4>Syntax</h4> | ||||||
| <li><pre>/search/query[<field><=|~><value>{(and|or)...]}/limit[<num>]/fields[<field1>,{<field2>}]/order[<field>]</pre> | <li><pre>/search/format=<format>/query=<field><=|~><value>{(and|or)...}/limit=<num>/fields=<field1>,{<field2>/order=<field></pre> | ||||||
| <h4>Example</h4> | <h4>Example</h4> | ||||||
| Search for entries with a call beginning with <b>M0</b> and a locator beginning with <b>I</b> or <b>J</b>, show the callsign and locator fields, order it by callsign and limit the results to <b>10</b>. | Search for entries with a call beginning with <b>M0</b> and a locator beginning with <b>I</b> or <b>J</b>, show the callsign and locator fields, order it by callsign and limit the results to <b>10</b>. | ||||||
| <li><pre>/search/query[Call~M0*(and)(Locator~I*(or)Locator~J*)]/limit[10]/fields[distinct(Call),Locator]/order[Call(asc)]</pre> | <li><pre>/search/format=xml/query=Call~M0*(and)(Locator~I*(or)Locator~J*)/limit=10/fields=distinct(Call),Locator/order=Call(asc)</pre> | ||||||
| <li><a href="<?php echo site_url('/api/search/query[Call~M0*(and)(Locator~I*(or)Locator~J*)]/limit[10]/fields[distinct(Call),Locator]/order[Call(asc)]'); ?>">Run it!</a> | <li><a href="<?php echo site_url('/api/search/format=xml/query=Call~M0*(and)(Locator~I*(or)Locator~J*)/limit=10/fields=distinct(Call),Locator/order=Call(asc)]'); ?>">Run it! (XML)</a> or <a href="<?php echo site_url('/api/search/format=json/query=Call~M0*(and)(Locator~I*(or)Locator~J*)/limit=10/fields=distinct(Call),Locator/order=Call(asc)'); ?>">Run it! (JSON)</a> | ||||||
| 
 | 
 | ||||||
| </div> | </div> | ||||||
|  |  | ||||||
|  | @ -1,17 +1,17 @@ | ||||||
| <?php | <?php | ||||||
| 
 | 
 | ||||||
| // Set the content-type for browsers
 |  | ||||||
| header("Content-type: text/xml"); |  | ||||||
| 
 |  | ||||||
| // Create the DOMDocument for the XML output
 | // Create the DOMDocument for the XML output
 | ||||||
| $xmlDoc = new DOMDocument("1.0"); | $xmlDoc = new DOMDocument("1.0"); | ||||||
| // Add reference to the XSLT
 | 
 | ||||||
| $xsl = $xmlDoc->createProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"/css/api.xsl\""); | if($data['format'] == "xml") { | ||||||
| $xmlDoc->appendChild($xsl); |     // Add reference to the XSLT
 | ||||||
|  |     $xsl = $xmlDoc->createProcessingInstruction("xml-stylesheet", "type=\"text/xsl\" href=\"/css/api.xsl\""); | ||||||
|  |     $xmlDoc->appendChild($xsl); | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // Get the method called, and build the root node
 | // Get the method called, and build the root node
 | ||||||
| $call = $data['queryInfo']['call']; | $call = $data['queryInfo']['call']; | ||||||
| $rootNode = $xmlDoc->createElement("HRDWebLogbook-API"); | $rootNode = $xmlDoc->createElement("Cloudlog-API"); | ||||||
| $parentNode = $xmlDoc->appendChild($rootNode); | $parentNode = $xmlDoc->appendChild($rootNode); | ||||||
| 
 | 
 | ||||||
| // Get the results output
 | // Get the results output
 | ||||||
|  | @ -36,14 +36,14 @@ $queryElement->setAttribute("executionTime", $data['queryInfo']['executionTime'] | ||||||
| $queryElement->setAttribute("logbookURL", $this->config->item('base_url')); | $queryElement->setAttribute("logbookURL", $this->config->item('base_url')); | ||||||
| 
 | 
 | ||||||
| // Add the main results node
 | // Add the main results node
 | ||||||
| $node = $xmlDoc->createElement("elements"); | $node = $xmlDoc->createElement("results"); | ||||||
| $elementsNode = $parentNode->appendChild($node); | $elementsNode = $parentNode->appendChild($node); | ||||||
| 
 | 
 | ||||||
| // Cycle through the results and add to the results node
 | // Cycle through the results and add to the results node
 | ||||||
| if($output['results']) | if($output['results']) | ||||||
| { | { | ||||||
| 	foreach($output['results'] as $e) { | 	foreach($output['results'] as $e) { | ||||||
| 		$node = $xmlDoc->createElement("element"); | 		$node = $xmlDoc->createElement("result"); | ||||||
| 		$element = $elementsNode->appendChild($node); | 		$element = $elementsNode->appendChild($node); | ||||||
| 
 | 
 | ||||||
| 		foreach($e as $attr) { | 		foreach($e as $attr) { | ||||||
|  | @ -70,8 +70,34 @@ if($output['results']) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Output formatted XML
 | if(isset($data['error'])) | ||||||
| echo formatXmlString($xmlDoc->saveXML()); | { | ||||||
|  |   $node = $xmlDoc->createElement("error"); | ||||||
|  |   $errorNode = $parentNode->appendChild($node); | ||||||
|  | 
 | ||||||
|  |   $errorNode->setAttribute("id", $data['error']); | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Output
 | ||||||
|  | 
 | ||||||
|  | // Check whether we want XML or JSON output
 | ||||||
|  | if(($data['format'] == "xml") || ($data['format'] == "xmlp") || ($data['format'] == "xmlt")) { | ||||||
|  |   if(($data['format'] == "xml") || ($data['format'] == "xmlp")) { | ||||||
|  |     // Set the content-type for browsers
 | ||||||
|  |     header("Content-type: text/xml"); | ||||||
|  |   } | ||||||
|  |   echo formatXmlString($xmlDoc->saveXML()); | ||||||
|  | } else if($data['format'] == "json") { | ||||||
|  |   // Set the content-type for browsers
 | ||||||
|  |   header("Content-type: application/json"); | ||||||
|  |   // For now, our JSON output is simply the XML re-parsed with SimpleXML and
 | ||||||
|  |   // then re-encoded with json_encode
 | ||||||
|  |   $x = simplexml_load_string($xmlDoc->saveXML()); | ||||||
|  |   $j = json_encode($x); | ||||||
|  |   echo $j; | ||||||
|  | } else { | ||||||
|  |   echo "Error: Unknown format type '".$data['format']."'."; | ||||||
|  | } | ||||||
| 
 | 
 | ||||||
| // This function tidies up the outputted XML
 | // This function tidies up the outputted XML
 | ||||||
| function formatXmlString($xml) { | function formatXmlString($xml) { | ||||||
|  |  | ||||||
|  | @ -11,11 +11,11 @@ | ||||||
|   <h1>Output of '<xsl:value-of select="//queryInfo/@calledMethod"/>'</h1> |   <h1>Output of '<xsl:value-of select="//queryInfo/@calledMethod"/>'</h1> | ||||||
|   <table id="results"> |   <table id="results"> | ||||||
|         <tr> |         <tr> | ||||||
|         <xsl:for-each select="//elements/element[1]/@*"> |         <xsl:for-each select="//results/result[1]/@*"> | ||||||
|           <th><b><xsl:value-of select="name()"/></b></th> |           <th><b><xsl:value-of select="name()"/></b></th> | ||||||
|         </xsl:for-each> |         </xsl:for-each> | ||||||
|         </tr> |         </tr> | ||||||
|         <xsl:for-each select="//elements/element"> |         <xsl:for-each select="//results/result"> | ||||||
| 		  <tr class="row{position() mod 2}"> | 		  <tr class="row{position() mod 2}"> | ||||||
|           <xsl:for-each select="@*"> |           <xsl:for-each select="@*"> | ||||||
|             <td><xsl:value-of select="."/></td> |             <td><xsl:value-of select="."/></td> | ||||||
|  |  | ||||||
		正在加载…
	
		在新工单中引用