Jump to content

How to get nested comments


Recommended Posts

Hi

I'm wondering how I can output nested comments.

I have written a viewthread.php file that works nicely so far. From index.php it gets the IDOfThread and passes this to a $number then outputs all the comments assigned to the Thread ID

However I'd like to be able to respond to each comment to create nested comments

This is my viewthread.php so far

<!DOCTYPE html>

<html> 
<header> 
<link href="styles.css" rel="stylesheet" type="text/css" media="all">
<title>test Image Board</title>

 <div class="header">
  <a href="index.php" class="logo">test forum online</a>
  <div class="header-right">
    <a class="active" href="#home">Home</a>
    <a href="#news">News</a>
    <a href="#contact">Education</a>
    <a href="#about">Tech</a>
  </div>
</div> 
</header> 
<body> 

<!-- <a href="CreateThread.php"> <h1>Create Thread</h1> </a> --> 


<div class ='Thread-grid-container'>
<?php
include 'dbconnect.php';
$number = intval($_GET['id']) ; 
session_start();
$_SESSION['id'] = $number ;
$query = mysqli_query($conn, "SELECT * FROM Threads where id=$number")
   or die (mysqli_error($conn));
//Output Grid layout for a Thread post
while ($row = mysqli_fetch_array($query)) {
//output picture from upload folder
        $imageURL = 'upload/'.rawurlencode($row["filename"]);
  echo
  
   "  <div class ='Thread-grid-item'>
<div class='ThreadNumber'> Post {$row['id']}<br> </div> 

      	<div class='UserOnThread'>{$row['Users']} </div> 
      	<h2>{$row['Title']} </h2> 

      	<button type='button' class ='collapse'>Hide</button>
		<div class ='img-block'> 
		 <img src={$row['$imageURL']}$imageURL alt='' />		
		</div> 
	<div class='bodytextThread'> <p>{$row['ThreadBody']}</p> </div>

		
      </div> 


    \n";
}?>

<div class="comment-upload-box"> 
<form action="CommentUpload.php" method="post" enctype="multipart/form-data">
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name="CommentText" cols="100" rows="10" > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name='submit' value="Submit"/></td>
<td></td>
</tr>
</table> 
</form>

</div>
<div class='divTableForComments'>
<div class='divTableBody'>

<?php 
include 'dbconnect.php';
//Output Comments onto page

//Create a variable for the Comment boxes so when clicking reply a text area shows 
$ChildCommentBoxes = "<div class='child-comment-upload-box' style='margin-left: 48px'> 
<form action='ChildCommentUpload.php' method='post' enctype'multipart/form-data'>
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name='ChildCommentText' cols='100' rows='10' > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type='submit' name='submit' value='Submit'/></td>
<td></td>
</tr>
</table> 
</form>";

$query = mysqli_query($conn, "SELECT * FROM Posts where IDOfThread=$number")
   or die (mysqli_error($conn));

while ($row = mysqli_fetch_array($query)) {
//May need this later to output pictures   
//     $imageURL = 'upload/'.rawurlencode($row["filename"]);
$CommentText = nl2br($row['CommentText']) ; 

$ParentComment = ""  ;
$replies = ""  ;
if (empty($row['ParentId'])) {

$ParentComment .=    "  <div class='divTableRow'>
	<div class='divTableCell'>{$row['User']} 
	<div class='pointsincommentbox'> {$row['Upvotes']}points</div>
	<div class='divTableComment'> 
		$CommentText <br>
	<div class='divCommentLinks'> 
		 <div class='upvotes'> ⬆</div> 
		<div class='upvotes'> ⬇</div> 
		<div> view comment </div> 
		<div>report </div> 
		<div>permalink</div> 
		<button type='button' class ='CommentChildButton'>reply</button>
		<div class ='OpenChildCommentBox'> 
		 $ChildCommentBoxes 	
		</div> 
	</div>

 </div>

</div>
</div> 
    \n";
}
    echo "$ParentComment "; 
}
?> 

<div class ="sidebar"> </div> 

</body> 
</html> 

<script>
var coll = document.getElementsByClassName("collapse");
var i;

for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


<script>
var coll2 = document.getElementsByClassName("CommentChildButton");
var i;

for (i = 0; i < coll2.length; i++) {
  coll2[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block	") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


And this is what it looks like image.thumb.png.ce46b660157c21ca88454da7c2815840.png

I think I would probably change the margin of the responses to jut them in a bit and am thinking the best way would be to create a function that cycles through and where it finds a ParentId of a comment matching the Posts.id it would then put this beneath it?

My comments are stored in a table titled Posts and Posts looks like

 

MariaDB [test]> describe Posts ; 
+---------------+-----------------+------+-----+---------------------+-------------------------------+
| Field         | Type            | Null | Key | Default             | Extra                         |
+---------------+-----------------+------+-----+---------------------+-------------------------------+
| id            | int(6) unsigned | NO   | PRI | NULL                | auto_increment                |
| User          | varchar(30)     | NO   |     | NULL                |                               |
| PostTimeStamp | timestamp       | NO   |     | current_timestamp() | on update current_timestamp() |
| CommentText   | varchar(8000)   | YES  |     | NULL                |                               |
| IDOfThread    | int(11)         | YES  |     | NULL                |                               |
| Upvotes       | int(11)         | NO   |     | 0                   |                               |
| ParentId      | int(11)         | YES  |     | NULL                |                               |
+---------------+-----------------+------+-----+---------------------+-------------------------------+
7 rows in set (0.002 sec)

 

Link to post
Share on other sites

Select all the posts for your thread in one sql statement.  As you loop over the query results, build a tree structure using an array that has all the posts nested under their parent post.  An easy way to do this is by using references.

$tree=[];
$map=[];
foreach ($queryResults as &$row){
	if ($row['ParentId']){
		$map[$row['ParentId']]['children'][] = &$row;
	} else {
		$row['children']=[];
		$tree[] = &$row;
	}

	$map[$row['id']] = &row;
}

After that the first level of $tree contains all your top level posts.  The child posts of each of them are stored in a key called 'children'.  To output them all you can use a recursive function.

function outputPostTree(array $tree){
	echo '<ul>';
	foreach ($tree as $post){
		echo '<li>'.$post['CommentText'];
		if ($post['children']){
			outputPostTree($post['children']);
		}
		echo '</li>';
	}
	echo '</ul>';
}

 

Link to post
Share on other sites

That is a really nice solution

I tried implementing it however and am not sure if I'm doing it correctly

 Also do I need to declare "children" as something?

<!DOCTYPE html>

<html> 
<header> 
<link href="styles.css" rel="stylesheet" type="text/css" media="all">
<title>Image Board</title>

 <div class="header">
  <a href="index.php" class="logo">forum ONLINE</a>
  <div class="header-right">
    <a class="active" href="#home">Home</a>
    <a href="#news">News</a>
    <a href="#contact">Education</a>
    <a href="#about">Tech</a>
  </div>
</div> 
</header> 
<body> 

<!-- <a href="CreateThread.php"> <h1>Create Thread</h1> </a> --> 


<div class ='Thread-grid-container'>
<?php
include 'dbconnect.php';
$number = intval($_GET['id']) ; 
session_start();
$_SESSION['id'] = $number ;
$query = mysqli_query($conn, "SELECT * FROM Threads where id=$number")
   or die (mysqli_error($conn));
//Output Grid layout for a Thread post
while ($row = mysqli_fetch_array($query)) {
//output picture from upload folder
        $imageURL = 'upload/'.rawurlencode($row["filename"]);
  echo
  
   "  <div class ='Thread-grid-item'>
<div class='ThreadNumber'> Post {$row['id']}<br> </div> 

      	<div class='UserOnThread'>{$row['Users']} </div> 
      	<h2>{$row['Title']} </h2> 

      	<button type='button' class ='collapse'>Hide</button>
		<div class ='img-block'> 
		 <img src={$row['$imageURL']}$imageURL alt='' />		
		</div> 
	<div class='bodytextThread'> <p>{$row['ThreadBody']}</p> </div>

		
      </div> 


    \n";
}?>

<div class="comment-upload-box"> 
<form action="CommentUpload.php" method="post" enctype="multipart/form-data">
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name="CommentText" cols="100" rows="10" > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name='submit' value="Submit"/></td>
<td></td>
</tr>
</table> 
</form>

</div>
<div class='divTableForComments'>
<div class='divTableBody'>

<?php 
include 'dbconnect.php';
//Output Comments onto page

//Create a variable for the Comment boxes so when clicking reply a text area shows 
$ChildCommentBoxes = "<div class='child-comment-upload-box' style='margin-left: 48px'> 
<form action='ChildCommentUpload.php' method='post' enctype'multipart/form-data'>
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name='ChildCommentText' cols='100' rows='10' > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type='submit' name='submit' value='Submit'/></td>
<td></td>
</tr>
</table> 
</form>";

$query = mysqli_query($conn, "SELECT * FROM Posts where IDOfThread=$number")
   or die (mysqli_error($conn));

while ($row = mysqli_fetch_array($query)) {
//May need this later to output pictures   
//     $imageURL = 'upload/'.rawurlencode($row["filename"]);
$CommentText = nl2br($row['CommentText']) ; 

$tree=[];
$map=[];
foreach ($queryResults as &$row){
	if ($row['ParentId']){
		$map[$row['ParentId']]['children'][] = &$row;
	} else {
		$row['children']=[];
		$tree[] = &$row;
	}

	$map[$row['id']] = &row;
}

$ParentComment = ""  ;
$replies = ""  ;
if (empty($row['ParentId'])) {

$ParentComment .=    "  <div class='divTableRow'>
	<div class='divTableCell'>{$row['User']} 
	<div class='pointsincommentbox'> {$row['Upvotes']}points</div>
	<div class='divTableComment'> 
		$CommentText <br>
	<div class='divCommentLinks'> 
		 <div class='upvotes'> ⬆</div> 
		<div class='upvotes'> ⬇</div> 
		<div> view comment </div> 
		<div>report </div> 
		<div>permalink</div> 
		<button type='button' class ='CommentChildButton'>reply</button>
		<div class ='OpenChildCommentBox'> 
		 $ChildCommentBoxes 	
		</div> 
	</div>

 </div>

</div>
</div> 
    \n";
     $ParentComment .=  outputPostTree($tree); 
}

echo "$ParentComment" ; 
}



function outputPostTree(array $tree){
	echo '<ul>';
	foreach ($tree as $post){
		echo '<li>'.$post['CommentText'];
		if ($post['children']){
			outputPostTree($post['children']);
		}
		echo '</li>';
	}
	echo '</ul>';
}
?> 



<div class ="sidebar"> </div> 

</body> 
</html> 

<script>
var coll = document.getElementsByClassName("collapse");
var i;

for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


<script>
var coll2 = document.getElementsByClassName("CommentChildButton");
var i;

for (i = 0; i < coll2.length; i++) {
  coll2[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block	") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>

 

Link to post
Share on other sites

So I did implement this but it doesn't output anything. The page does load but the comments don't get spat out.

when running at the command line "php viewthread2.php" I get this back however?

PHP Warning:  Undefined variable $tree in /var/www/html/Backup16042020/viewthread2.php on line 129
PHP Fatal error:  Uncaught TypeError: outputPostTree(): Argument #1 ($tree) must be of type array, null given, called in /var/www/html/Backup16042020/viewthread2.php on line 129 and defined in /var/www/html/Backup16042020/viewthread2.php:132
Stack trace:
#0 /var/www/html/Backup16042020/viewthread2.php(129): outputPostTree()
#1 {main}
  thrown in /var/www/html/Backup16042020/viewthread2.php on line 132

 

with line 132 being the function declaration

function outputPostTree(array $tree){

 

 

 

<!DOCTYPE html>

<html> 
<header> 
<link href="styles.css" rel="stylesheet" type="text/css" media="all">
<title> Image Board</title>

 <div class="header">
  <a href="index.php" class="logo"> ARMY ONLINE</a>
  <div class="header-right">
    <a class="active" href="#home">Home</a>
    <a href="#news">News</a>
    <a href="#contact">Education</a>
    <a href="#about">Tech</a>
  </div>
</div> 
</header> 
<body> 

<!-- <a href="CreateThread.php"> <h1>Create Thread</h1> </a> --> 


<div class ='Thread-grid-container'>
<?php
include 'dbconnect.php';
$number = intval($_GET['id']) ; 
session_start();
$_SESSION['id'] = $number ;
$query = mysqli_query($conn, "SELECT * FROM Threads where id=$number")
   or die (mysqli_error($conn));
//Output Grid layout for a Thread post
while ($row = mysqli_fetch_array($query)) {
//output picture from upload folder
        $imageURL = 'upload/'.rawurlencode($row["filename"]);
  echo
  
   "  <div class ='Thread-grid-item'>
<div class='ThreadNumber'> Post {$row['id']}<br> </div> 

      	<div class='UserOnThread'>{$row['Users']} </div> 
      	<h2>{$row['Title']} </h2> 

      	<button type='button' class ='collapse'>Hide</button>
		<div class ='img-block'> 
		 <img src={$row['$imageURL']}$imageURL alt='' />		
		</div> 
	<div class='bodytextThread'> <p>{$row['ThreadBody']}</p> </div>

		
      </div> 


    \n";
}?>

<div class="comment-upload-box"> 
<form action="CommentUpload.php" method="post" enctype="multipart/form-data">
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name="CommentText" cols="100" rows="10" > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name='submit' value="Submit"/></td>
<td></td>
</tr>
</table> 
</form>

</div>
<div class='divTableForComments'>
<div class='divTableBody'>

<?php 
include 'dbconnect.php';
//Output Comments onto page

//Create a variable for the Comment boxes so when clicking reply a text area shows 
$ChildCommentBoxes = "<div class='child-comment-upload-box' style='margin-left: 48px'> 
<form action='ChildCommentUpload.php' method='post' enctype'multipart/form-data'>
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name='ChildCommentText' cols='100' rows='10' > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type='submit' name='submit' value='Submit'/></td>
<td></td>
</tr>
</table> 
</form>";

$query = mysqli_query($conn, "SELECT * FROM Posts where IDOfThread=$number")
   or die (mysqli_error($conn));

while ($row = mysqli_fetch_array($query)) {
//May need this later to output pictures   
//     $imageURL = 'upload/'.rawurlencode($row["filename"]);
$CommentText = nl2br($row['CommentText']) ; 

$tree=[];
$map=[];
foreach ($query as &$row){
	if ($row['ParentId']){
		$map[$row['ParentId']]['children'][] = &$row;
	} else {
		$row['children']=[];
		$tree[] = &$row;
	}

	$map[$row['id']] = &$row;
}
    
}
    echo outputPostTree($tree) ; 


function outputPostTree(array $tree){
	echo '<ul>';
	foreach ($tree as $post){
		echo '<li>'.$post['CommentText'];
		if ($post['children']){
			outputPostTree($post['children']);
		}
		echo '</li>';
	}
	echo '</ul>';
}
?> 

<div class ="sidebar"> </div> 

</body> 
</html> 

<script>
var coll = document.getElementsByClassName("collapse");
var i;

for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


<script>
var coll2 = document.getElementsByClassName("CommentChildButton");
var i;

for (i = 0; i < coll2.length; i++) {
  coll2[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block	") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


<script>

document.getElementById("upvote").onclick = function(){
	onmouseover = document.body.style.cursor = "pointer";
	document.getElementById("upvote").style.color = 'orange';
}
</script>

 

 

 

Link to post
Share on other sites

In the original example:

foreach ($queryResults as &$row){

represents the loop for your query results processing.  That'd be your while loop.  So you'd replace the foreach with your while loop, not put the foreach inside your loop.  Using the while loop means you have to unset($row) at the end to break the reference before the next iteration also, otherwise the first row data would just keep getting overwritten by future rows.  The foreach handles this by using & before the variable, the while loop doesn't.

$tree=[];
$map=[];
while ($row = mysqli_fetch_array($query)) {
    //whatever else you need to do ...

    if ($row['ParentId']){
        $map[$row['ParentId']]['children'][] = &$row;
    } else {
        $row['children']=[];
        $tree[] = &$row;
    }

    $map[$row['id']] = &row;
    unset($row); // break the reference for next iteration.
}

Your error is a result of your query returning zero rows and the way you had your code structured.  Since you just copy/pasted my code into your loop rather than integrating it, $tree is never declared when no rows are returned by the query making it essentially NULL.  Since the function wants an array and not NULL that causes an error to be thrown.

Link to post
Share on other sites
  • 2 weeks later...

So I implemented it like this and my page loads fine however it doesn't retrieve any of the comments

Also looking at the code you wrote should I have a children column on the my Posts table(comments table)?

Also when I comment on my site?

 

<!DOCTYPE html>

<html> 
<header> 
<link href="styles.css" rel="stylesheet" type="text/css" media="all">
<title> Image Board</title>

 <div class="header">
  <a href="index.php" class="logo">ONLINE</a>
  <div class="header-right">
    <a class="active" href="#home">Home</a>
    <a href="#news">News</a>
    <a href="#contact">Education</a>
    <a href="#about">Tech</a>
  </div>
</div> 
</header> 
<body> 

<!-- <a href="CreateThread.php"> <h1>Create Thread</h1> </a> --> 


<div class ='Thread-grid-container'>
<?php
include 'dbconnect.php';
$number = intval($_GET['id']) ; 
session_start();
$_SESSION['id'] = $number ;
$query = mysqli_query($conn, "SELECT * FROM Threads where id=$number")
   or die (mysqli_error($conn));
//Output Grid layout for a Thread post
while ($row = mysqli_fetch_array($query)) {
//output picture from upload folder
        $imageURL = 'upload/'.rawurlencode($row["filename"]);
  //Check if filename is a pdf otherwise output for image 
  if(strpos($imageURL, "pdf") !== false){
	echo   "  <div class ='Thread-grid-item'>
<div class='ThreadNumber'> Post {$row['id']}<br> </div> 

      	<div class='UserOnThread'>{$row['Users']} </div> 
      	<h2>{$row['Title']} </h2> 

      	<button type='button' class ='collapse'>Hide</button>
      			<div class ='img-block'> 
		 <a href=$imageURL> <img src='Icons/PDF.jpg' alt='' /> </a>		
		 </div>	
	<div class='bodytextThread'> <p>{$row['ThreadBody']}</p> </div>

		
      </div> 


    \n";
    

} else{
	echo   "  <div class ='Thread-grid-item'>
<div class='ThreadNumber'> Post {$row['id']}<br> </div> 

      	<div class='UserOnThread'>{$row['Users']} </div> 
      	<h2>{$row['Title']} </h2> 

      	<button type='button' class ='collapse'>Hide</button>
		<div class ='img-block'> 
		 <img src=$imageURL alt='' />		
		</div> 
	<div class='bodytextThread'> <p>{$row['ThreadBody']}</p> </div>

		
      </div> 


    \n";
   
}      
        
  
  

}?>

<div class="comment-upload-box"> 
<form action="CommentUpload.php" method="post" enctype="multipart/form-data">
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name="CommentText" cols="100" rows="10" > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type="submit" name='submit' value="Submit"/></td>
<td></td>
</tr>
</table> 
</form>

</div>
<div class='divTableForComments'>
<div class='divTableBody'>

<?php 
include 'dbconnect.php';
//Output Comments onto page

//Create a variable for the Comment boxes so when clicking reply a text area shows 
$ChildCommentBoxes = "<div class='child-comment-upload-box' style='margin-left: 48px'> 
<form action='ChildCommentUpload.php' method='post' enctype'multipart/form-data'>
<table>
<tr>
<td></td>
</tr>
<tr>
<td>Comment: </td>
<td> <textarea name='ChildCommentText' cols='100' rows='10' > Enter your posts... 
</textarea>
 </td>
<td></td>
</tr>
<tr>
<td></td>
<td><input type='submit' name='submit' value='Submit'/></td>
<td></td>
</tr>
</table> 
</form>";

$query = mysqli_query($conn, "SELECT * FROM Posts where IDOfThread=$number")
   or die (mysqli_error($conn));
$tree=[];
$map=[];
foreach ($queryResults as &$row){
	if ($row['ParentId']){
		$map[$row['ParentId']]['children'][] = &$row;
	} else {
		$row['children']=[];
		$tree[] = &$row;
	}

	$map[$row['id']] = &$row;
}

function outputPostTree(array $tree){
	echo '<ul>';
	foreach ($tree as $post){
		echo '<li>'.$post['CommentText'];
		if ($post['children']){
			outputPostTree($post['children']);
		}
		echo '</li>';
	}
	echo '</ul>';
}

?> 

<div class ="sidebar"> </div> 

</body> 
</html> 

<script>
var coll = document.getElementsByClassName("collapse");
var i;

for (i = 0; i < coll.length; i++) {
  coll[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


<script>
var coll2 = document.getElementsByClassName("CommentChildButton");
var i;

for (i = 0; i < coll2.length; i++) {
  coll2[i].addEventListener("click", function() {
    this.classList.toggle("active");
    var content = this.nextElementSibling;
    if (content.style.display === "block	") {
      content.style.display = "none";
    } else {
      content.style.display = "block";
    }
  });
}
</script>


<script>

document.getElementById("upvote").onclick = function(){
	onmouseover = document.body.style.cursor = "pointer";
	document.getElementById("upvote").style.color = 'orange';
}
</script>

 

Link to post
Share on other sites

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.