Having trouble getting some code to auto-capitalize words properly

OK, I left it out of the wrapper, since I can’t test it in the wrapper.

There may be a better way, but this is the first working version I came up with…
[php]<?php
global $exclude_words, $capital_words, $max_length;
$data = “this is my hyphen-word sentence. that better be OK with you! lol”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$max_length = 5; // Maximum word length, anything over is case-lowered

global $exclude_words, $capital_words, $max_length ;

$words = explode(" ",$data);
foreach($words as $word)
  {
    $ignore = $do = $chunk = array();
    $part = 0;
    for($i=0;$i<strlen($word);$i++)
      {
        $char = ord($word[$i]);
        if($char<65 || ($char>90 && $char<97) || $char>122)
          {
            $ignore[++$part] = chr($char);
            $part++;
          }
            
        else $do[$part] .= chr($char);
      }

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        if(!in_array(strtolower($wordpart),$exclude_words))
          {
            if(in_array(strtolower($wordpart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array(strtolower($wordpart),$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords($wordpart);
          }
        else $changed[$k] = strtolower($wordpart);        
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
 }

echo implode(’ ',$string);[/php]

This could continue to grow indefinitely! For instance, what about foreign character sets, etc.

Let me know if this works for you, and if you see any other rules or changes that need to be implemented!

I didn’t comment the code, so if you want any explanations please let me know, I’d be happy to do so.

Thank you for continuing to help!! :slight_smile:

This seems to work except for words like ‘what’s’ or ‘doesn’t’ where it takes the single letter after the apostrophe and capitalizes it. Instead of outputting ‘What’s’ it outputs ‘What’S’.

Thank you again!

Another thing I’ve noticed in testing is that if I use the word ‘a’ as in ‘A good test’ or any of my exclude_words, it will have them all lowercase even if it is the first word in the sentence. Typing ‘The best sentence’ => 'the Best Sentence".

I’m beginning to think I’m going about this all wrong. Am I? It seems you’re right about it getting more and more complex.

Does PHP have a simple way to test if the first word in a sentence belongs to $exclude_words and then ignore that its in $exclude_words and capitalize the first letter of it just like any other normal word?

Thank you for bearing with me as I try to learn this stuff.

My pleasure!

This version seems to handle single quotes correctly. We will need to specify any other characters that we want to treat as part of a single word as well. If there are very many, we might want to move them to an array.

Let me know…[php]<?php
global $exclude_words, $capital_words, $max_length;
$data = “this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know…”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$max_length = 5; // Maximum word length, anything over is case-lowered

global $exclude_words, $capital_words, $max_length ;

$words = explode(" ",$data);
foreach($words as $word)
  {
    $ignore = $do = $chunk = array();
    $part = 0;
    for($i=0;$i<strlen($word);$i++)
      {
        $char = ord($word[$i]);
        if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
          {
            $ignore[++$part] = chr($char);
            $part++;
          }
            
        else $do[$part] .= chr($char);
      }

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        if(!in_array(strtolower($wordpart),$exclude_words))
          {
            if(in_array(strtolower($wordpart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array(strtolower($wordpart),$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords($wordpart);
          }
        else $changed[$k] = strtolower($wordpart);        
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
 }

echo implode(’ ',$string);[/php]

Malasho,

Thank you again.

That does work with the single quotes!

Not sure if you missed my previous post, but I figure I will bug you one more time if that is ok. :wink:

Does PHP have a simple way to test if the first word in a sentence belongs to $exclude_words and then ignore that its in $exclude_words and capitalize the first letter of it just like any other normal word? So if it saw that we had ‘the’ in $exclude_words but a sentence was ‘the milk is in the fridge’ then instead of the current output of ‘the Milk Is in the Fridge’ it would do ‘The Milk Is in the Fridge’, capitalizing the first instance of ‘the’ since it is the first word in the sentence and thereby excluding it from the $exclude_words list.

Thank you!

See how this works:[php]<?php
global $exclude_words, $capital_words, $max_length;
$data = “this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

global $exclude_words, $capital_words, $max_length ;
$start = 1;

$words = explode(" ",$data);
foreach($words as $word)
  {
    $ignore = $do = $chunk = array();
    $part = 0;
    for($i=0;$i<strlen($word);$i++)
      {
        $char = ord($word[$i]);
        if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
          {
            $ignore[++$part] = chr($char);
            $part++;
          }
        else $do[$part] .= chr($char);
      }

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        if(!in_array(strtolower($wordpart),$exclude_words) || $start == 1)
          {
            if(in_array(strtolower($wordpart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array(strtolower($wordpart),$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords($wordpart);
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $start = in_array(substr(end($string),-1),$start_indicators);
 }

echo implode(’ ',$string);[/php]

Wow you are amazingly helpful!!! ;D

It is so close to being perfect!

Just reading over your code is so informative. I see how you did the start=0 to figure out the first word.

How can we make it so that if the first word is in $exclude_words, we ucwords it. So if I type in 'THE best expert ever!" it becomes ‘The Best Expert Ever!’

I think with that this code would be perfect. I really can’t thank you enough.

That was my mistake! I forgot to include the strtolower when I retyped a line.

Lets give this a try. (Make sure it didn’t break any of the other rules, I can’t remember any that I believe it would have changed).[php]<?php
global $exclude_words, $capital_words, $max_length;
$data = “THE solution is at hand. this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

global $exclude_words, $capital_words, $max_length ;
$start = 1;

$words = explode(" ",$data);
foreach($words as $word)
  {
    $ignore = $do = $chunk = array();
    $part = 0;
    for($i=0;$i<strlen($word);$i++)
      {
        $char = ord($word[$i]);
        if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
          {
            $ignore[++$part] = chr($char);
            $part++;
          }
        else $do[$part] .= chr($char);
      }

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        if(!in_array(strtolower($wordpart),$exclude_words) || $start == 1)
          {
            if(in_array(strtolower($wordpart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array(strtolower($wordpart),$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $start = in_array(substr(end($string),-1),$start_indicators);
 }

echo implode(’ ',$string);[/php]

There’s some pretty weird stuff going on in this script and perhaps there is a better way to do it, but off hand, this was the best I could think of. We may look back at this script and see a totally different and better approach. This is the kind of code I really enjoy revisiting periodically to see if I can come up with a better way.

This works wonderfully!

I am going to try to wrap my forum stuff around it and see if I can get it working. :slight_smile:

So I’ve gone ahead and wrapped it around my forum script, and I think I did it correctly but I may be missing something.

I changed the echo implode(’ ',$string); to $data = implode(" ",$string); and merged the other stuff but I’m getting the following error when I try to run it:

Warning: in_array() [function.in-array]: Wrong datatype for second argument

It gives me a line number for a different file since this code is being run “inside” that file so I don’t think the line number would be of much help.

[php]if (is_subclass_of($this, ‘vB_DataManager_ThreadPost’) && is_array($this->validfields[‘title’]))
{
global $exclude_words, $capital_words, $max_length;
//$data = “THE solution is at hand. this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

$this->validfields['title'][VF_CODE] = '
    global $exclude_words, $capital_words, $max_length ;
	$retval = $dm->verify_title($data);
	$start = 1;
	
    $words = explode(" ",$data);
	foreach($words as $word)
  {
    $ignore = $do = $chunk = array();
    $part = 0;
    for($i=0;$i<strlen($word);$i++)
      {
        $char = ord($word[$i]);
        if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
          {
            $ignore[++$part] = chr($char);
            $part++;
          }
        else $do[$part] .= chr($char);
      }

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        if(!in_array(strtolower($wordpart),$exclude_words) || $start == 1)
          {
            if(in_array(strtolower($wordpart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array(strtolower($wordpart),$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $start = in_array(substr(end($string),-1),$start_indicators);
 }

                
$data = implode(" ",$string);

return $retval;
';

}[/php]

Googling the error led me to this page: http://www.daniweb.com/web-development/php/threads/70710/in_array-wrong-datatype-for-second-argument but I’m not sure if I’m just incorporating your code incorrectly since your code does work fine on the standalone php file. It only gives the error once I try to merge it into my code.

See if this works by chance:[php]if (is_subclass_of($this, ‘vB_DataManager_ThreadPost’) && is_array($this->validfields[‘title’]))
{
global $exclude_words, $capital_words, $max_length;
//$data = “THE solution is at hand. this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

$this->validfields['title'][VF_CODE] = '
    global $exclude_words, $capital_words, $max_length ;

$retval = $dm->verify_title($data);

$start = 1;

    $words = explode(" ",$data);

foreach($words as $word)
{
$ignore = $do = $chunk = array();
$part = 0;
for($i=0;$i<strlen($word);$i++)
{
$char = ord($word[$i]);
if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
{
$ignore[++$part] = chr($char);
$part++;
}
else $do[$part] .= chr($char);
}

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        if(!in_array(strtolower($wordpart),$exclude_words) || $start == 1)
          {
            if(in_array(strtolower($wordpart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array(strtolower($wordpart),$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $puncTest = substr(end($string),-1);
    $start = in_array($puncTest,$start_indicators);
 }

                
$data = implode(" ",$string);

return $retval;
';

}[/php]

It may have been having problems with processing the string inside the in_array.

Thank you again for the response.

It is giving the same error unfortunately even with the new code. :frowning:

Lets replace every function in the in_arrays and see if that works:[php]if (is_subclass_of($this, ‘vB_DataManager_ThreadPost’) && is_array($this->validfields[‘title’]))
{
global $exclude_words, $capital_words, $max_length;
//$data = “THE solution is at hand. this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

$this->validfields['title'][VF_CODE] = '
    global $exclude_words, $capital_words, $max_length ;

$retval = $dm->verify_title($data);

$start = 1;

    $words = explode(" ",$data);

foreach($words as $word)
{
$ignore = $do = $chunk = array();
$part = 0;
for($i=0;$i<strlen($word);$i++)
{
$char = ord($word[$i]);
if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
{
$ignore[++$part] = chr($char);
$part++;
}
else $do[$part] .= chr($char);
}

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        $lcWordPart = strtolower($wordpart);
        if(!in_array($lcWordPart,$exclude_words) || $start == 1)
          {
            if(in_array($lcWordPart),$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array($lcWordPart,$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $puncTest = substr(end($string),-1);
    $start = in_array($puncTest,$start_indicators);
 }

                
$data = implode(" ",$string);

return $retval;
';

}[/php]

Parse error: syntax error, unexpected ‘,’

and

Fatal error: Function name must be a string

DOH ???

I ended up with an extra parenthesis in the first in_array. Lets give this a shot:[php]if (is_subclass_of($this, ‘vB_DataManager_ThreadPost’) && is_array($this->validfields[‘title’]))
{
global $exclude_words, $capital_words, $max_length;
//$data = “THE solution is at hand. this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

$this->validfields['title'][VF_CODE] = '
    global $exclude_words, $capital_words, $max_length ;

$retval = $dm->verify_title($data);

$start = 1;

    $words = explode(" ",$data);

foreach($words as $word)
{
$ignore = $do = $chunk = array();
$part = 0;
for($i=0;$i<strlen($word);$i++)
{
$char = ord($word[$i]);
if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
{
$ignore[++$part] = chr($char);
$part++;
}
else $do[$part] .= chr($char);
}

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        $lcWordPart = strtolower($wordpart);
        if(!in_array($lcWordPart,$exclude_words) || $start == 1)
          {
            if(in_array($lcWordPart,$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array($lcWordPart,$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $puncTest = substr(end($string),-1);
    $start = in_array($puncTest,$start_indicators);
 }

                
$data = implode(" ",$string);

return $retval;
';

}[/php]

Ok the previous two errors are gone but this is the one we’re getting again:

Warning: in_array() [function.in-array]: Wrong datatype for second argument

OK, I think I see the problem.

When it got wrapped into your forum script, all the arrays were left outside of it. I think this should work:[php]if (is_subclass_of($this, ‘vB_DataManager_ThreadPost’) && is_array($this->validfields[‘title’]))
{
$this->validfields[‘title’][VF_CODE] = ’
global $exclude_words, $capital_words, $max_length;
//$data = “THE solution is at hand. this is my hyphen-word sentence. that better be OK with you! lol. Now it is imprOVED and HANDles doesn’t, don’t you know… and now it excludes excluded words when they follow certain characters!”;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

$retval = $dm->verify_title($data);

$start = 1;

    $words = explode(" ",$data);

foreach($words as $word)
{
$ignore = $do = $chunk = array();
$part = 0;
for($i=0;$i<strlen($word);$i++)
{
$char = ord($word[$i]);
if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
{
$ignore[++$part] = chr($char);
$part++;
}
else $do[$part] .= chr($char);
}

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        $lcWordPart = strtolower($wordpart);
        if(!in_array($lcWordPart,$exclude_words) || $start == 1)
          {
            if(in_array($lcWordPart,$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array($lcWordPart,$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $puncTest = substr(end($string),-1);
    $start = in_array($puncTest,$start_indicators);
 }

                
$data = implode(" ",$string);

return $retval;
';

}[/php]

If it does, you could go back and put the functions back into their places in the if_arrays or just leave it as is.

Ok now the error message is this:

Parse error: syntax error, unexpected T_STRING

Lets try this:[php]if (is_subclass_of($this, ‘vB_DataManager_ThreadPost’) && is_array($this->validfields[‘title’]))
{
$this->validfields[‘title’][VF_CODE] = ’
global $exclude_words, $capital_words, $max_length;
$exclude_words = array(“a”,“an”,“and”,“at”,“but”,“by”,“for”,“in”,“nor”,“of”,“on”,“or”,“so”,“the”,“to”,“up”,“yet”); // Exclude analyzing these words
$capital_words = array(“brb”,“lol”,“usa”); // Capital exclusives (leave in lowercase in array)
$start_indicators = array(".","?","!");
$max_length = 5; // Maximum word length, anything over is case-lowered

$retval = $dm->verify_title($data);

$start = 1;

    $words = explode(" ",$data);

foreach($words as $word)
{
$ignore = $do = $chunk = array();
$part = 0;
for($i=0;$i<strlen($word);$i++)
{
$char = ord($word[$i]);
if($char!= 39 && ($char<65 || ($char>90 && $char<97) || $char>122))
{
$ignore[++$part] = chr($char);
$part++;
}
else $do[$part] .= chr($char);
}

    $changed = array();
    foreach ($do as $k=>$wordpart)
      {
        $lcWordPart = strtolower($wordpart);
        if(!in_array($lcWordPart,$exclude_words) || $start == 1)
          {
            if(in_array($lcWordPart,$capital_words)) $changed[$k] = strtoupper($wordpart);
            elseif(strlen($wordpart) >=$max_length && !in_array($lcWordPart,$capital_words)) $changed[$k] = ucwords(strtolower($wordpart));
            else $changed[$k] = ucwords(strtolower($wordpart));
            $start = 0;
          }
        else $changed[$k] = strtolower($wordpart);
      }
      
    $chunk = $changed+$ignore;
    ksort($chunk);
    $string[] = implode($chunk);
    $puncTest = substr(end($string),-1);
    $start = in_array($puncTest,$start_indicators);
 }

                
$data = implode(" ",$string);

return $retval;
';

}[/php]

Sorry… I don’t have any way of testing with the wrapper…

Sponsor our Newsletter | Privacy Policy | Terms of Service