- add sqBodyWrap utility functions to rewrap entire body rather than one line at...
authorbraverock <braverock@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Sun, 6 Jun 2004 14:28:48 +0000 (14:28 +0000)
committerbraverock <braverock@7612ce4b-ef26-0410-bec9-ea0150e637f0>
Sun, 6 Jun 2004 14:28:48 +0000 (14:28 +0000)
  - improves old sqWordWrap for reply
  - patch contributed by Justus Pendleton

git-svn-id: https://svn.code.sf.net/p/squirrelmail/code/trunk/squirrelmail@7606 7612ce4b-ef26-0410-bec9-ea0150e637f0

functions/strings.php
src/compose.php

index 6db1afa8364b9f5b67cc82734d9fa168535a337e..27dfa8a1f96cecbe28cfc061369296c20ab1bf5a 100644 (file)
@@ -33,6 +33,237 @@ $SQM_INTERNAL_VERSION = array(1,5,1);
  */
 require_once(SM_PATH . 'functions/global.php');
 
+/**
+ * Appends citation markers to the string.
+ * Also appends a trailing space.
+ *
+ * @author Justus Pendleton
+ *
+ * @param string str The string to append to
+ * @param int citeLevel the number of markers to append
+ * @return null
+ */
+function sqMakeCite (&$str, $citeLevel) {
+    for ($i = 0; $i < $citeLevel; $i++) {
+        $str .= '>';
+    }
+    if ($citeLevel != 0) {
+        $str .= ' ';
+    }
+}
+
+/**
+ * Create a newline in the string, adding citation
+ * markers to the newline as necessary.
+ *
+ * @author Justus Pendleton
+ *
+ * @param string str the string to make a newline in
+ * @param int citeLevel the citation level the newline is at
+ * @param int column starting column of the newline
+ * @return null
+ */
+function sqMakeNewLine (&$str, $citeLevel, &$column) {
+    $str .= "\n";
+    $column = 0;
+    if ($citeLevel > 0) {
+        sqMakeCite ($str, $citeLevel);
+        $column = $citeLevel + 1;
+    } else {
+        $column = 0;
+    }
+}
+
+/**
+ * Wraps text at $wrap characters.  While sqWordWrap takes
+ * a single line of text and wraps it, this function works
+ * on the entire corpus at once, this allows it to be a little
+ * bit smarter and when and how to wrap.
+ *
+ * @author Justus Pendleton
+ *
+ * @param string body the entire body of text
+ * @param int wrap the maximum line length
+ * @return string the wrapped text
+ */
+function &sqBodyWrap (&$body, $wrap) {
+    // the newly wrapped text
+    $outString = '';
+    // current column since the last newline in the outstring
+    $outStringCol = 0;
+    $length = strlen($body);
+    // where we are in the original string
+    $pos = 0;
+    // the number of >>> citation markers we are currently at
+    $citeLevel = 0;
+
+    // the main loop, whenever we start a newline of input text
+    // we start from here
+    while ($pos < $length) {
+       // we're at the beginning of a line, get the new cite level
+       $newCiteLevel = 0;
+
+       while (($pos < $length) && ($body{$pos} == '>')) {
+           $newCiteLevel++;
+           $pos++;
+
+           // skip over any spaces interleaved among the cite markers
+           while (($pos < $length) && ($body{$pos} == ' ')) {
+               $pos++;
+           }
+           if ($pos >= $length) {
+               break;
+           }
+       }
+
+       // special case: if this is a blank line then maintain it
+       // (i.e. try to preserve original paragraph breaks)
+       // unless they occur at the very beginning of the text
+       if (($body{$pos} == "\n") && (strlen($outString) != 0)) {
+           $outStringLast = $outString{strlen($outString) - 1};
+           if ($outStringLast != "\n") {
+               $outString .= "\n";
+           }
+           sqMakeCite ($outString, $newCiteLevel);
+           $outString .= "\n";
+           $pos++;
+           $outStringCol = 0;
+           continue;
+       }
+
+       // if the cite level has changed, then start a new line
+       // with the new cite level.
+       if (($citeLevel != $newCiteLevel) && ($pos > ($newCiteLevel + 1)) && ($outStringCol != 0)) {
+           sqMakeNewLine ($outString, 0, $outStringCol);
+       }
+
+       $citeLevel = $newCiteLevel;
+
+       // prepend the quote level if necessary
+       if ($outStringCol == 0) {
+           sqMakeCite ($outString, $citeLevel);
+           // if we added a citation then move the column
+           // out by citelevel + 1 (the cite markers + the space)
+           $outStringCol = $citeLevel + ($citeLevel ? 1 : 0);
+       } else if ($outStringCol > $citeLevel) {
+           // not a cite and we're not at the beginning of a line
+           // in the output.  add a space to separate the new text
+           // from previous text.
+           $outString .= ' ';
+           $outStringCol++;
+       }
+
+       // find the next newline -- we don't want to go further than that
+       $nextNewline = strpos ($body, "\n", $pos);
+       if ($nextNewline === FALSE) {
+           $nextNewline = $length;
+       }
+
+       // Don't wrap unquoted lines at all.  For now the textarea
+       // will work fine for this.  Maybe revisit this later though
+       // (for completeness more than anything else, I think)
+       if ($citeLevel == 0) {
+           $outString .= substr ($body, $pos, ($nextNewline - $pos));
+           $outStringCol = $nextNewline - $pos;
+           if ($nextNewline != $length) {
+               sqMakeNewLine ($outString, 0, $outStringCol);
+           }
+           $pos = $nextNewline + 1;
+           continue;
+       }
+
+       // inner loop, (obviously) handles wrapping up to
+       // the next newline
+       while ($pos < $nextNewline) {
+           // skip over initial spaces
+           while (($pos < $nextNewline) && (ctype_space ($body{$pos}))) {
+               $pos++;
+           }
+
+           // if this is a short line then just append it and continue outer loop
+           if (($outStringCol + $nextNewline - $pos) <= ($wrap - $citeLevel - 1)) {
+               // if this is the final line in the input string then include
+               // any trailing newlines
+               if (($nextNewline + 1 == $length) && ($body{$nextNewline} == "\n")) {
+                   $nextNewline++;
+               }
+
+               // trim trailing spaces
+               $lastRealChar = $nextNewline;
+               while (($lastRealChar > $pos) && (ctype_space ($body{$lastRealChar}))) {
+                   $lastRealChar--;
+               }
+
+               $outString .= substr ($body, $pos, ($lastRealChar - $pos + 1));
+               $outStringCol += ($lastRealChar - $pos);
+               $pos = $nextNewline + 1;
+               continue;
+           }
+
+           $eol = $pos + $wrap - $citeLevel - $outStringCol;
+           // eol is the tentative end of line.
+           // look backwards for there for a whitespace to break at.
+           // if it's already less than our current position then
+           // our current line is already too long, break immediately
+           // and restart outer loop
+           if ($eol <= $pos) {
+               sqMakeNewLine ($outString, $citeLeve, $outStringCol);
+               continue;
+           }
+
+           // start looking backwards for whitespace to break at.
+           $breakPoint = $eol;
+           while (($breakPoint > $pos) && (! ctype_space ($body{$breakPoint}))) {
+               $breakPoint--;
+           }
+
+           // if we didn't find a breakpoint by looking backward then we
+           // need to figure out what to do about that
+           if ($breakPoint == $pos) {
+               // if we are not at the beginning then end this line
+               // and start a new loop
+               if ($outStringCol > ($citeLevel + 1)) {
+                   sqMakeNewLine ($outString, $citeLevel, $outStringCol);
+                   continue;
+               } else {
+                   // just hard break here.  most likely we are breaking
+                   // a really long URL.  could also try searching
+                   // forward for a break point, which is what Mozilla
+                   // does.  don't bother for now.
+                   $breakPoint = $eol;
+               }
+           }
+
+           // special case: maybe we should have wrapped last
+           // time.  if the first breakpoint here makes the
+           // current line too long and there is already text on
+           // the current line, break and loop again if at
+           // beginning of current line, don't force break
+           $SLOP = 6;
+           if ((($outStringCol + ($breakPoint - $pos)) > ($wrap + $SLOP)) && ($outStringCol > ($citeLevel + 1))) {
+               sqMakeNewLine ($outString, $citeLevel, $outStringCol);
+               continue;
+           }
+
+           // skip newlines or whitespace at the beginning of the string
+           $substring = substr ($body, $pos, ($breakPoint - $pos));
+           $substring = rtrim ($substring); // do rtrim and ctype_space have the same ideas about whitespace?
+           $outString .= $substring;
+           $outStringCol += strlen ($substring);
+           // advance past the whitespace which caused the wrap
+           $pos = $breakPoint;
+           while (($pos < $length) && (ctype_space ($body{$pos}))) {
+               $pos++;
+           }
+           if ($pos < $length) {
+               sqMakeNewLine ($outString, $citeLevel, $outStringCol);
+           }
+       }
+    }
+
+    return $outString;
+}
+
 /**
  * Wraps text at $wrap characters
  *
@@ -310,8 +541,8 @@ function OneTimePadDecrypt ($string, $epad) {
 /**
  * Randomizes the mt_rand() function.
  *
- * Toss this in strings or integers and it will seed the generator 
- * appropriately. With strings, it is better to get them long. 
+ * Toss this in strings or integers and it will seed the generator
+ * appropriately. With strings, it is better to get them long.
  * Use md5() to lengthen smaller strings.
  *
  * @param mixed val a value to seed the random number generator
@@ -515,14 +746,14 @@ function makeComposeLink($url, $text = null, $target='')
     }
 
 
-    // if not using "compose in new window", make 
+    // if not using "compose in new window", make
     // regular link and be done with it
     if($compose_new_win != '1') {
         return makeInternalLink($url, $text, $target);
     }
 
 
-    // build the compose in new window link...  
+    // build the compose in new window link...
 
 
     // if javascript is on, use onClick event to handle it
@@ -566,14 +797,14 @@ function sm_print_r() {
  * version of fwrite which checks for failure
  */
 function sq_fwrite($fp, $string) {
-       // write to file
-       $count = @fwrite($fp,$string);
-       // the number of bytes written should be the length of the string
-       if($count != strlen($string)) {
-               return FALSE;
-       }
-
-       return $count;
+        // write to file
+        $count = @fwrite($fp,$string);
+        // the number of bytes written should be the length of the string
+        if($count != strlen($string)) {
+                return FALSE;
+        }
+
+        return $count;
 }
 
 /**
@@ -587,11 +818,11 @@ function sq_fwrite($fp, $string) {
  *                <li>HTML_SPECIALCHARS - html special characters table</li>
  *             </ul>
  * @param integer $quote_style quote encoding style. Possible values (without quotes):
- *             <ul>
+ *              <ul>
  *                <li>ENT_COMPAT - (default) encode double quotes</li>
  *                <li>ENT_NOQUOTES -  don't encode double or single quotes</li>
  *                <li>ENT_QUOTES - encode double and single quotes</li>
- *             </ul>
+ *              </ul>
  * @param string $charset charset used for encoding. default to us-ascii, 'auto' uses $default_charset global value.
  * @return array html translation array
  */
@@ -605,21 +836,21 @@ function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='
 
   // < and >
   $sq_html_ent_table = array_merge($sq_html_ent_table,
-                       array("<" => '&lt;',
-                             ">" => '&gt;')
-                       );
+                        array("<" => '&lt;',
+                              ">" => '&gt;')
+                        );
   // double quotes
   if ($quote_style == ENT_COMPAT)
      $sq_html_ent_table = array_merge($sq_html_ent_table,
-                           array("\"" => '&quot;')
-                           );
+                            array("\"" => '&quot;')
+                            );
 
   // double and single quotes
   if ($quote_style == ENT_QUOTES)
      $sq_html_ent_table = array_merge($sq_html_ent_table,
-                           array("\"" => '&quot;',
-                             "'" => '&#39;')
-                           );
+                            array("\"" => '&quot;',
+                              "'" => '&#39;')
+                            );
 
   if ($charset=='auto') $charset=$default_charset;
 
@@ -647,11 +878,11 @@ function sq_get_html_translation_table($table,$quote_style=ENT_COMPAT,$charset='
  *
  * @param string $string string that has to be sanitized
  * @param integer $quote_style quote encoding style. Possible values (without quotes):
- *             <ul>
+ *              <ul>
  *                <li>ENT_COMPAT - (default) encode double quotes</li>
  *                <li>ENT_NOQUOTES - don't encode double or single quotes</li>
  *                <li>ENT_QUOTES - encode double and single quotes</li>
- *             </ul>
+ *              </ul>
  * @param string $charset charset used for encoding. defaults to 'us-ascii', 'auto' uses $default_charset global value.
  * @return string sanitized string
  */
index 5852039a1ff40cac40de60a3e55a76bf9d39f94e..9b83c0168eaf0205e2c619c2dde86e54391a4be4 100644 (file)
@@ -372,6 +372,9 @@ if ($send) {
          * Rewrap $body so that no line is bigger than $editor_size
          * This should only really kick in the sqWordWrap function
          * if the browser doesn't support "VIRTUAL" as the wrap type.
+         *
+         * (braverock:) It is unclear if this code should be replaced
+         * with sqBodyWrap or removed entirely.
          */
         $body = explode("\n", $body);
         $newBody = '';
@@ -386,7 +389,6 @@ if ($send) {
                 $newBody .= $line . "\n";
 
             }
-
         }
         $body = $newBody;
 
@@ -790,14 +792,12 @@ function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $se
             /* this corrects some wrapping/quoting problems on replies */
             $rewrap_body = explode("\n", $body);
             $from =  (is_array($orig_header->from)) ? $orig_header->from[0] : $orig_header->from;
-            sqUnWordWrap($body);    // unwrap and then reset it?!
             $body = '';
             $strip_sigs = getPref($data_dir, $username, 'strip_sigs');
             foreach ($rewrap_body as $line) {
                 if ($strip_sigs && substr($line,0,3) == '-- ') {
             break;
                 }
-                sqWordWrap($line, ($editor_size));
                 if (preg_match("/^(>+)/", $line, $matches)) {
                     $gt = $matches[1];
                     $body .= $body_quote . str_replace("\n", "\n$body_quote$gt ", rtrim($line)) ."\n";
@@ -805,6 +805,10 @@ function newMail ($mailbox='', $passed_id='', $passed_ent_id='', $action='', $se
                     $body .= $body_quote . (!empty($body_quote) ? ' ' : '') . str_replace("\n", "\n$body_quote" . (!empty($body_quote) ? ' ' : ''), rtrim($line)) . "\n";
                 }
             }
+
+            //rewrap the body to clean up quotations and line lengths
+            $body = sqBodyWrap ($body, $editor_size);
+
             $body = getReplyCitation($from , $orig_header->date) . $body;
             $composeMessage->reply_rfc822_header = $orig_header;