Log note : there indicates place, their indicates ownership
changed:
-
Introduction

 For those people that would like to have a spell checker in their tracker,
well here is one using **aspell**.

The spell checker is only executed for the web interface and not for the mail
gateway. I guess it's safe to assume that e-mail clients can do the spell check
for us, so there's no need to check the spelling on issues received from the
mail gateway.

The spell checker will check all words with exception of upper case words.
Those will be skipped. In some cases we want to skip the spell check for
all words. Therefor a check box can be checked. If checked, the spell check
is skipped and all text is accepted as it is.<br>
If a spell check is performed and spelling mistakes are found, the form will
be returned with an enumeration of all misspelled words. If available, then
suggestions will be given for those words to.<br>
To make it easier to lookup those misspelled words, they will be marked with
stars like &#42;word&#42;. Just go trough the text, correct the marked words
(or write them in uppercase to skip them) and submit the issue again. And
if you don't want the words to be skipped in uppercase, then just correct
the words which need to be corrected, check the 'No spell checking' check box
and re-submit the form.

Requirements

 To use this feature, the next software if required on your server:

<ul>
<li> aspell (http://aspell.sourceforge.net)
<li> aspell-python (http://www.republika.pl/wmula/proj/aspell-python/index.html)
</ul>

Implementation

 The spell checker is implemented in two action handlers and not in auditors.
This ensures us that the spell checker will only execute for the
web interface and not for the mail gateway.<br>
The new action handlers only do the spell checker part. The rest of the handling
is still done by the original roundup action handlers. This will keep our new
action handlers plain and simple.

Both action handlers are almost the same. The difference is that one is for
new issues while the other one is for updating an issue.<br>
To use the action handlers, only minor changes are required.
Regretfully the classic 'issue.item.html' template uses a roundup method to
put the *submit* button on the form together with the required hidden input
fields that will tell roundup what to do, so we need to make some small
changes to call the spell checker action handler in 'issue.item.html'.
Even so do we need to tell the action handler which form fields to check.
This is done by adding an hidden input tag to the template. This hidden
tag looks like:<br>
&nbsp;&nbsp;&nbsp;<code>&lt;input type="hidden" name="@aspell" value="&lt;name&gt;:&lt;field&gt;,..."&gt;</code><br>
For the default classic template it will be:<br>
&nbsp;&nbsp;&nbsp;<code>&lt;input type="hidden" name="@aspell" value="title:title,@note:message"&gt;</code><br>
The above line will perform spell checking on HTML form fields *title* and *@note*.
If errors are found in these fields, the error message will refer to the fields
*title* and *message* (like they are named on the page).

Best regards,<br>
Marlon van den Berg

<hr>

Source Code

 These are the two new action handlers. Copy the content to a file and save
it in folder '&lt;tracker_home&gt;/extensions'::

    class AspellClass:
        def check(self):
            """\
            Check if a form input field has some spelling mistakes.

            If it has, it returns a tuple with a dictionary on the
            first tuple index and the marked up text on the second
            tuple index.

            The keys of the dictionary contain all the spelling
            mistakes. The items contain a tuple with suggestions
            for the word.

            The marked up text is the original input form field
            with '*' around the spelling mistakes.
            """

            def __inner(text):
                """\
                Does all the work.
                """

                import aspell
                import re

                # select english language
                sp = aspell.Speller('lang', 'en') 

                # split the text into list of words
                # (excluding punctuation marks)
                word_list = re.split('\W+', text)

                new_text = text

                errors = {}
                for word in word_list:
                    # skip empty words (not words at all) and
                    # skip upercase words
                    if word and not word.isupper():
                        word = word.lower()

                        # if it isn't checked before
                        if not errors.has_key(word):
                            # perform spellcheck on word
                            if not sp.check(word):
                                # word incorrect
                                suggestions = sp.suggest(word)

                                if suggestions:
                                    # put spelling mistake in error list with suggestions
                                    errors[word] = suggestions

                                    # put some markings around the word
                                    res = re.compile('\\b(%s)\\b'%word, re.IGNORECASE)
                                    new_text = res.sub('*\\1*', new_text)

                return (errors, new_text)

            if self.form.has_key('no_spell_check') \
            and self.form['no_spell_check'].value == '1':
                # no spell check requested
                return None

            errors = []

            if self.form.has_key('@aspell'):
                _aspell = '@aspell'
            elif self.form.has_key(':aspell'):
                _aspell = ':aspell'
            else:
                _aspell = None

            if _aspell:
                for field in self.form[_aspell].value.split(','):
                    form_name, nice_name = field.split(':')

                    if self.form.has_key(form_name):
                        new_errors, text = __inner(self.form[form_name].value)

                        if new_errors:
                            # update form text (will place the markings around
                            # the spelling mistakes)
                            self.form[form_name].value = text

                            if errors:
                                # more than one form field has a spelling mistake
                                errors.append('<br>')

                            # tell the user which form field has the spelling mistake
                            errors.append("<i>%s</i> contains spelling error(s):"%nice_name)

                            # add each spelling mistake to the error message
                            for word, suggest in new_errors.items():
                                errors.append('''<br>\
        &nbsp;&nbsp;<b style="color:yellow">%s</b> - <select><option>%s</option></select>'''% \
        (word, '</option><option>'.join(suggest)))

                self.form.value.remove(self.form[_aspell])

            return errors

    class Aspell_NewItemAction(AspellClass, NewItemAction):
        def handle(self):
            # perform spell check
            errors = self.check()

            if errors:
                # we have some spelling mistakes
                self.client.error_message.extend(errors)
                return

            else:
                # let RoundUp do the rest
                NewItemAction.handle(self)

    class Aspell_EditItemAction(AspellClass, actions.EditItemAction):
        def handle(self):
            # perform spell check
            errors = self.check()

            if errors:
                # we have some spelling mistakes
                self.client.error_message.extend(errors)
                return

            else:
                # let RoundUp do the rest
                actions.EditItemAction.handle(self)

    def init(instance):
        instance.registerAction('aspell_new_item', Aspell_NewItemAction)
        instance.registerAction('aspell_edit_item', Aspell_EditItemAction)

Here are the changes for the 'issue.item.html' template.<br>
Lookup this part::

     <td colspan=3>
      <span tal:replace="structure context/submit">submit button</span>
      <a tal:condition="context/id" tal:attributes="href context/copy_url"
       i18n:translate="">Make a copy</a>
     </td>

and replace it with::

     <td colspan=3>
      <tal:block tal:condition="not:context/id">
       <input type="hidden" name="@action" value="aspell_new">
       <input type="submit" name="submit" value="Submit New Entry">
      </tal:block>
      <tal:block tal:condition="context/id">
       <input type="hidden" name="@lastactivity" tal:attributes="value context/activity">
       <input type="hidden" name="@action" value="aspell_edit">
       <input type="submit" name="submit" value="Submit Changes">
       <a tal:attributes="href context/copy_url" i18n:translate="">Make a copy</a>
      </tal:block>
      <input type="hidden" name="@aspell" value="title:title,@note:message">
      <input type="checkbox" name="no_spell_check" value="1">No spell checking
     </td>