HTML fix
[squirrelmail.git] / plugins / squirrelspell / modules / check_me.mod
1 <?php
2 /**
3  * check_me.mod
4  * -------------
5  * Squirrelspell module.
6  *
7  * Copyright (c) 1999-2005 The SquirrelMail Project Team
8  * Licensed under the GNU GPL. For full terms see the file COPYING.
9  *
10  * This module is the main workhorse of SquirrelSpell. It submits
11  * the message to the spell-checker, parses the output, and loads
12  * the interface window.
13  *
14  * @author Konstantin Riabitsev <icon@duke.edu>
15  * @version $Id$
16  * @package plugins
17  * @subpackage squirrelspell
18  */
19
20 /**
21  * This function makes a javascript-powered link. Not sure why
22  * Philippe decided to move it outside the main code, but hey. ;)
23  * I bet for the i18n reasons.
24  *
25  * @param  $jscode Javascript code to include in the link.
26  * @param  $title  A little pop-up title to provide for the links.
27  * @param  $link   The content of the link.
28  * @return         void, since this just draws the content.
29  */
30 function SpellLink($jscode, $title, $link) {
31   echo "<td><a href=\"javascript:$jscode\" "
32     . "title=\"$title\">$link</a>"
33     . '</td>';
34 }
35
36 /**
37  * Declaring globals for users with E_ALL set.
38  */
39 global $SQSPELL_APP_DEFAULT, $SQSPELL_APP, $attachment_dir, $color;
40
41 if (! sqgetGlobalVar('sqspell_text',$sqspell_text,SQ_POST)) {
42   $sqspell_text = '';
43 }
44 if (! sqgetGlobalVar('sqspell_use_app',$sqspell_use_app,SQ_POST)) {
45   $sqspell_use_app = $SQSPELL_APP_DEFAULT;
46 }
47
48 /**
49  * Now we explode the lines for three reasons:
50  * 1) So we can ignore lines starting with ">" (reply's)
51  * 2) So we can stop processing when we get to "--" on a single line,
52  *    which means that the signature is starting
53  * 3) So we can add an extra space at the beginning of each line. This way
54  *    ispell/aspell don't treat these as command characters.
55  */
56 $sqspell_raw_lines = explode("\n", $sqspell_text);
57 for ($i=0; $i<sizeof($sqspell_raw_lines); $i++){
58   /**
59    * See if the signature is starting, which will be a "--" on the
60    * single line (after trimming).
61    */
62   if (trim($sqspell_raw_lines[$i]) == '--'){
63     break;
64   }
65   /**
66    * See if this is quoted text. Don't check the quoted text, since
67    * it's no business of ours how badly our correspondents misspell
68    * stuff.
69    */
70   if(substr($sqspell_raw_lines[$i], 0, 1) != '>'){
71     $sqspell_new_lines[$i] = ' ' . $sqspell_raw_lines[$i];
72   } else {
73     $sqspell_new_lines[$i] = ' ';
74   }
75 }
76 /**
77  * $sqspell_new_lines array now contains the lines to submit to the
78  * spellchecker.
79  */
80 $sqspell_new_text=implode("\n", $sqspell_new_lines);
81
82 /**
83  * Define the command used to spellcheck the document.
84  */
85 $sqspell_command=$SQSPELL_APP[$sqspell_use_app];
86 /**
87  * If you have php >= 4.3.0, we can use proc_open and safe mode
88  * and not mess w/ temp files.  Otherwise we will do it the old
89  * way, (minus the uneeded call to cat that messes up Wintel
90  * boxen.)
91  * Thanks Ray Ferguson for providing this patch.
92  */
93 if( check_php_version ( 4, 3 ) ) {
94     $descriptorspec = array(
95        0 => array('pipe', 'r'),  // stdin is a pipe that the child will read from
96        1 => array('pipe', 'w'),  // stdout is a pipe that the child will write to
97        2 => array('pipe', 'w'), // stderr is a pipe that the child will write to
98     );
99     $spell_proc = @proc_open($sqspell_command, $descriptorspec, $pipes);
100     if ( ! is_resource ( $spell_proc ) ) {
101         error_box ( _("Could not run the spellchecker command (%s).",
102             htmlspecialchars($sqspell_command) ) , $color );
103     }
104     if ( ! @fwrite($pipes[0], $sqspell_new_text) ) {
105         error_box ( _("Error while writing to pipe.",
106             htmlspecialchars($floc) ) , $color );
107     }
108     fclose($pipes[0]);
109     $sqspell_output = array();
110     for($i=1; $i<=2; $i++) {
111         while(!feof($pipes[$i])) {
112            array_push($sqspell_output, rtrim(fgetss($pipes[$i],999),"\n"));
113         }
114         fclose($pipes[$i]);
115     }
116     $sqspell_exitcode=proc_close($spell_proc);
117 } else {
118     do {
119       $floc = "$attachment_dir/" . md5($sqspell_new_text . microtime());
120     } while (file_exists($floc));
121     $fp = @fopen($floc, 'w');
122     if ( ! is_resource ($fp) ) {
123         error_box ( _("Could not open temporary file '%s'.",
124             htmlspecialchars($floc) ) , $color );
125     }
126     if ( ! @fwrite($fp, $sqspell_new_text) ) {
127         error_box ( _("Error while writing to temporary file '%s'.",
128             htmlspecialchars($floc) ) , $color );
129     }
130     fclose($fp);
131     exec("$sqspell_command < $floc 2>&1", $sqspell_output, $sqspell_exitcode);
132     unlink($floc);
133 }
134
135 /**
136  * Check if the execution was successful. Bail out if it wasn't.
137  */
138 if ($sqspell_exitcode){
139   $msg= "<div align='center'>"
140      . sprintf(_("I tried to execute '%s', but it returned:"),
141                $sqspell_command) . "<pre>"
142      . htmlspecialchars(join("\n", $sqspell_output)) . '</pre>'
143      . '<form onsubmit="return false">'
144      . '<input type="submit" value="  ' . _("Close")
145      . '  " onclick="self.close()" /></form></div>';
146   sqspell_makeWindow(null, _("SquirrelSpell is misconfigured."), null, $msg);
147   exit;
148 }
149
150 /**
151  * Load the user dictionary.
152  */
153 $words=sqspell_getLang($sqspell_use_app);
154 /**
155  * Define some variables to be used during the processing.
156  */
157 $current_line=0;
158 $missed_words=Array();
159 $misses = Array();
160 $locations = Array();
161 $errors=0;
162 /**
163  * Now we process the output of sqspell_command (ispell or aspell in
164  * ispell compatibility mode, whichever). I'm going to be scarce on
165  * comments here, since you can just look at the ispell/aspell output
166  * and figure out what's going on. ;) The best way to describe this is
167  * "Dark Magic".
168  */
169 for ($i=0; $i<sizeof($sqspell_output); $i++){
170   switch (substr($sqspell_output[$i], 0, 1)){
171   /**
172    * Line is empty.
173    * Ispell adds empty lines when an end of line is reached
174    */
175   case '':
176     $current_line++;
177   break;
178   /**
179    * Line begins with "&".
180    * This means there's a misspelled word and a few suggestions.
181    */
182   case '&':
183     list($left, $right) = explode(": ", $sqspell_output[$i]);
184     $tmparray = explode(" ", $left);
185     $sqspell_word=$tmparray[1];
186     /**
187      * Check if the word is in user dictionary.
188      */
189     if (! in_array($sqspell_word,$words)){
190       $sqspell_symb=intval($tmparray[3])-1;
191       if (!isset($misses[$sqspell_word])) {
192         $misses[$sqspell_word] = $right;
193         $missed_words[$errors] = $sqspell_word;
194         $errors++;
195       }
196       if (isset($locations[$sqspell_word])){
197         $locations[$sqspell_word] .= ', ';
198       } else {
199         $locations[$sqspell_word] = '';
200       }
201       $locations[$sqspell_word] .= "$current_line:$sqspell_symb";
202     }
203   break;
204   /**
205    * Line begins with "#".
206    * This means a misspelled word and no suggestions.
207    */
208   case '#':
209     $tmparray = explode(" ", $sqspell_output[$i]);
210     $sqspell_word=$tmparray[1];
211     /**
212      *
213      * Check if the word is in user dictionary.
214      */
215     if (!in_array($sqspell_word,$words)){
216       $sqspell_symb=intval($tmparray[2])-1;
217       if (!isset($misses[$sqspell_word])) {
218           $misses[$sqspell_word] = '_NONE';
219           $missed_words[$errors] = $sqspell_word;
220           $errors++;
221       }
222       if (isset($locations[$sqspell_word])) {
223           $locations[$sqspell_word] .= ', ';
224       } else {
225           $locations[$sqspell_word] = '';
226       }
227       $locations[$sqspell_word] .= "$current_line:$sqspell_symb";
228     }
229   break;
230   }
231 }
232
233 if ($errors){
234   /**
235    * Load the spelling errors into JavaScript arrays
236    * (More dark magic!)
237    */
238   $extrajs="<script type=\"text/javascript\">\n"
239     . "<!--\n";
240
241   $sqspell_lines = explode("\n", $sqspell_text);
242   /**
243    * The javascript array sqspell_lines[] contains all lines of
244    * the message we've been checking.
245    */
246   $extrajs.= "var sqspell_lines=new Array();\n";
247   for ($i=0; $i<sizeof($sqspell_lines); $i++){
248     $extrajs.= "sqspell_lines[$i] = \""
249       . chop(addslashes($sqspell_lines[$i])) . "\";\n";
250   }
251   $extrajs.= "\n\n";
252
253   /**
254    * The javascript array misses[] contais all misspelled words.
255    */
256   $extrajs.= "var misses=new Array();\n";
257   for ($i=0; $i<sizeof($missed_words); $i++){
258     $extrajs.= "misses[$i] = \"" . $missed_words[$i] . "\";\n";
259   }
260   $extrajs.= "\n\n";
261
262   /**
263    * Suggestions are (guess what!) suggestions for misspellings
264    */
265   $extrajs.= "var suggestions = new Array();\n";
266   $i=0;
267   while (list($word, $value) = each($misses)){
268     if ($value=='_NONE') $value='';
269     $extrajs.= "suggestions[$i] = \"$value\";\n";
270     $i++;
271   }
272   $extrajs.= "\n\n";
273
274   /**
275    * Locations are where those misspellings are located, line:symbol
276    */
277   $extrajs.= "var locations= new Array();\n";
278   $i=0;
279   while (list($word, $value) = each($locations)){
280     $extrajs.= "locations[$i] = \"$value\";\n";
281     $i++;
282   }
283
284   /**
285    * Add some strings so they can be i18n'd.
286    */
287   $extrajs.= "var ui_completed = \"" . _("Spellcheck completed. Commit changes?")
288     . "\";\n";
289   $extrajs.= "var ui_nochange = \"" . _("No changes were made.") . "\";\n";
290   $extrajs.= "var ui_wait = \""
291     . _("Now saving your personal dictionary... Please wait.")
292     . "\";\n";
293
294
295   /**
296    * Did I mention that I hate dots on the end of contcatenated lines?
297    * Dots at the beginning make so much more sense!
298    */
299   $extrajs.= "//-->\n"
300     . "</script>\n"
301     . "<script src=\"js/check_me.js\" type=\"text/javascript\"></script>\n";
302
303
304   displayHtmlHeader(_("SquirrelSpell Results"),$extrajs);
305
306   echo "<body bgcolor=\"$color[4]\" text=\"$color[8]\" link=\"$color[7]\" "
307     . "alink=\"$color[7]\" vlink=\"$color[7]\" "
308     . "onload=\"populateSqspellForm()\">\n";
309   ?>
310   <table width="100%" border="0" cellpadding="2">
311    <tr>
312     <td bgcolor="<?php echo $color[9] ?>" align="center">
313      <b>
314       <?php printf( ngettext(_("Found %d error"),_("Found %d errors"),$errors), $errors ) ?>
315      </b>
316     </td>
317    </tr>
318    <tr>
319     <td>
320       <hr />
321     </td>
322    </tr>
323    <tr>
324     <td>
325      <form method="post">
326       <input type="hidden" name="MOD" value="forget_me_not" />
327       <input type="hidden" name="words" value="" />
328       <input type="hidden" name="sqspell_use_app"
329              value="<?php echo $sqspell_use_app ?>" />
330       <table border="0" width="100%">
331        <tr align="center">
332         <td colspan="4">
333          <?php
334           $sptag = "<span style=\"background-color: $color[9]\">";
335           echo $sptag . _("Line with an error:") . '</span>';
336          ?>
337          <br />
338          <textarea name="sqspell_line_area" cols="50" rows="3"
339                    wrap="hard" onfocus="this.blur()"></textarea>
340         </td>
341        </tr>
342        <tr valign="middle">
343         <td align="right" width="25%">
344          <?php
345           echo $sptag . _("Error:") . '</span>';
346          ?>
347         </td>
348         <td align="left" width="25%">
349          <input name="sqspell_error" size="10" value=""
350                 onfocus="this.blur()" />
351         </td>
352         <td align="right" width="25%">
353          <?php
354           echo $sptag . _("Suggestions:") . '</span>';
355          ?>
356         </td>
357         <td align="left" width="25%">
358          <select name="sqspell_suggestion"
359                  onchange="if (this.options[this.selectedIndex].value != '_NONE') document.forms[0].sqspell_oruse.value=this.options[this.selectedIndex].value">
360           <?php
361            echo '<option>' . _("Suggestions") . '</option>';
362           ?>
363          </select>
364         </td>
365        </tr>
366        <tr>
367         <td align="right">
368          <?php
369           echo $sptag . _("Change to:") . '</span>';
370          ?>
371         </td>
372         <td align="left">
373          <input name="sqspell_oruse" size="15" value=""
374                 onfocus="if(!this.value) this.value=document.forms[0].sqspell_error.value" />
375         </td>
376         <td align="right">
377          <?php
378           echo $sptag . _("Occurs times:") . '</span>';
379          ?>
380         </td>
381         <td align="left">
382          <input name="sqspell_likethis" size=3 value="" onfocus="this.blur()" />
383         </td>
384        </tr>
385         <!-- hello? What is this? </td></tr> -->
386        <tr>
387         <td colspan="4"><hr /></td>
388        </tr>
389        <tr>
390         <td colspan="4">
391          <table border="0" cellpadding="0" cellspacing="3" width="100%">
392           <tr align="center" bgcolor="<?php echo $color[9] ?>">
393            <?php
394            SpellLink('sqspellChange()',
395                    _("Change this word"),
396                    _("Change"));
397          SpellLink('sqspellChangeAll()',
398                  _("Change ALL occurances of this word"),
399                  _("Change All"));
400          SpellLink('sqspellIgnore()',
401                  _("Ignore this word"),
402                  _("Ignore"));
403          SpellLink('sqspellIgnoreAll()',
404                  _("Ignore ALL occurances this word"),
405                  _("Ignore All"));
406          SpellLink('sqspellRemember()',
407                  _("Add this word to your personal dictionary"),
408                  _("Add to Dic"));
409            ?>
410           </tr>
411          </table>
412         </td>
413        </tr>
414        <tr>
415         <td colspan="4"><hr /></td>
416        </tr>
417        <tr>
418         <td colspan="4" align="center" bgcolor="<?php echo $color[9] ?>">
419          <?php
420              echo '<input type="button" value="  '
421                  . _("Close and Commit")
422                  . '  " onclick="if (confirm(\''
423                  . _("The spellcheck is not finished. Really close and commit changes?")
424                  . '\')) sqspellCommitChanges()" />'
425                  . ' <input type="button" value="  '
426                  . _("Close and Cancel")
427                  . '  " onclick="if (confirm(\''
428                  . _("The spellcheck is not finished. Really close and discard changes?")
429                  . '\')) self.close()" />';
430          ?>
431         </td>
432        </tr>
433       </table>
434      </form>
435     </td>
436    </tr>
437   </table>
438   </body></html>
439   <?php
440 } else {
441   /**
442    * AREN'T YOU SUCH A KNOW-IT-ALL!
443    */
444   $msg='<form onsubmit="return false"><div align="center">' .
445        '<input type="submit" value="  ' . _("Close") .
446        '  " onclick="self.close()" /></div></form>';
447   sqspell_makeWindow(null, _("No errors found"), null, $msg);
448 }
449
450 /**
451  * For Emacs weenies:
452  * Local variables:
453  * mode: php
454  * End:
455  * vim: syntax=php et ts=4
456  */
457 ?>