diff options
author | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2015-06-15 21:16:42 +0000 |
---|---|---|
committer | Jean-Philippe Lang <jp_lang@yahoo.fr> | 2015-06-15 21:16:42 +0000 |
commit | 3ae42cb32617670cb6c99a60f5cda2cf961d110c (patch) | |
tree | 2348af8e781435538aed5517b0c2087432b42c78 | |
parent | 7fb35abd10e3da24188f0f4325d4ba001687ebac (diff) | |
download | redmine-3ae42cb32617670cb6c99a60f5cda2cf961d110c.tar.gz redmine-3ae42cb32617670cb6c99a60f5cda2cf961d110c.zip |
Better handle html-only emails (#16962).
git-svn-id: http://svn.redmine.org/redmine/trunk@14313 e93f8b46-1217-0410-a6f0-8f06a7374b81
-rw-r--r-- | Gemfile | 1 | ||||
-rw-r--r-- | app/models/mail_handler.rb | 16 | ||||
-rw-r--r-- | lib/redmine.rb | 7 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting.rb | 18 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting/html_parser.rb | 54 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting/markdown/html_parser.rb | 40 | ||||
-rw-r--r-- | lib/redmine/wiki_formatting/textile/html_parser.rb | 41 | ||||
-rw-r--r-- | test/fixtures/mail_handler/outlook_2010_html_only.eml | 966 | ||||
-rw-r--r-- | test/fixtures/mail_handler/outlook_web_access_2010_html_only.eml | 65 | ||||
-rw-r--r-- | test/fixtures/mail_handler/ticket_html_only.eml | 3 | ||||
-rw-r--r-- | test/unit/lib/redmine/wiki_formatting/html_parser_test.rb | 30 | ||||
-rw-r--r-- | test/unit/lib/redmine/wiki_formatting/markdown_html_parser_test.rb | 30 | ||||
-rw-r--r-- | test/unit/lib/redmine/wiki_formatting/textile_html_parser_test.rb | 30 | ||||
-rw-r--r-- | test/unit/mail_handler_test.rb | 30 |
14 files changed, 1312 insertions, 19 deletions
@@ -13,6 +13,7 @@ gem "mime-types" gem "protected_attributes" gem "actionpack-action_caching" gem "actionpack-xml_parser" +gem "loofah", "~> 2.0" # Windows does not include zoneinfo files, so bundle the tzinfo-data gem gem 'tzinfo-data', platforms: [:mingw, :x64_mingw, :mswin, :jruby] diff --git a/app/models/mail_handler.rb b/app/models/mail_handler.rb index 78cff699b..5ea370e2a 100644 --- a/app/models/mail_handler.rb +++ b/app/models/mail_handler.rb @@ -433,14 +433,11 @@ class MailHandler < ActionMailer::Base @plain_text_body = parts.map do |p| body_charset = Mail::RubyVer.respond_to?(:pick_encoding) ? Mail::RubyVer.pick_encoding(p.charset).to_s : p.charset - Redmine::CodesetUtil.to_utf8(p.body.decoded, body_charset) - end.join("\r\n") - # strip html tags and remove doctype directive - if parts.any? {|p| p.mime_type == 'text/html'} - @plain_text_body = strip_tags(@plain_text_body.strip) - @plain_text_body.sub! %r{^<!DOCTYPE .*$}, '' - end + body = Redmine::CodesetUtil.to_utf8(p.body.decoded, body_charset) + # convert html parts to text + p.mime_type == 'text/html' ? self.class.html_body_to_text(body) : body + end.join("\r\n") @plain_text_body end @@ -454,6 +451,11 @@ class MailHandler < ActionMailer::Base subject.strip[0,255] end + # Converts a HTML email body to text + def self.html_body_to_text(html) + Redmine::WikiFormatting.html_parser.to_text(html) + end + def self.assign_string_attribute_with_limit(object, attribute, value, limit=nil) limit ||= object.class.columns_hash[attribute.to_s].limit || 255 value = value.to_s.slice(0, limit) diff --git a/lib/redmine.rb b/lib/redmine.rb index 6e3149dea..e00d089f5 100644 --- a/lib/redmine.rb +++ b/lib/redmine.rb @@ -267,11 +267,8 @@ Redmine::Search.map do |search| end Redmine::WikiFormatting.map do |format| - format.register :textile, Redmine::WikiFormatting::Textile::Formatter, Redmine::WikiFormatting::Textile::Helper - if Object.const_defined?(:Redcarpet) - format.register :markdown, Redmine::WikiFormatting::Markdown::Formatter, Redmine::WikiFormatting::Markdown::Helper, - :label => 'Markdown' - end + format.register :textile + format.register :markdown if Object.const_defined?(:Redcarpet) end ActionView::Template.register_template_handler :rsb, Redmine::Views::ApiTemplateHandler diff --git a/lib/redmine/wiki_formatting.rb b/lib/redmine/wiki_formatting.rb index c4e4bb7c3..4fd5ea8e8 100644 --- a/lib/redmine/wiki_formatting.rb +++ b/lib/redmine/wiki_formatting.rb @@ -28,12 +28,19 @@ module Redmine yield self end - def register(name, formatter, helper, options={}) + def register(name, *args) + options = args.last.is_a?(Hash) ? args.pop : {} name = name.to_s raise ArgumentError, "format name '#{name}' is already taken" if @@formatters[name] + + formatter, helper, parser = args.any? ? + args : + %w(Formatter Helper HtmlParser).map {|m| "Redmine::WikiFormatting::#{name.classify}::#{m}".constantize} + @@formatters[name] = { :formatter => formatter, :helper => helper, + :html_parser => parser, :label => options[:label] || name.humanize } end @@ -42,6 +49,10 @@ module Redmine formatter_for(Setting.text_formatting) end + def html_parser + html_parser_for(Setting.text_formatting) + end + def formatter_for(name) entry = @@formatters[name.to_s] (entry && entry[:formatter]) || Redmine::WikiFormatting::NullFormatter::Formatter @@ -52,6 +63,11 @@ module Redmine (entry && entry[:helper]) || Redmine::WikiFormatting::NullFormatter::Helper end + def html_parser_for(name) + entry = @@formatters[name.to_s] + (entry && entry[:html_parser]) || Redmine::WikiFormatting::HtmlParser + end + def format_names @@formatters.keys.map end diff --git a/lib/redmine/wiki_formatting/html_parser.rb b/lib/redmine/wiki_formatting/html_parser.rb new file mode 100644 index 000000000..9d83497bd --- /dev/null +++ b/lib/redmine/wiki_formatting/html_parser.rb @@ -0,0 +1,54 @@ +# Redmine - project management software +# Copyright (C) 2006-2015 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require 'loofah/helpers' + +module Redmine + module WikiFormatting + class HtmlParser + + class_attribute :tags + self.tags = { + 'br' => {:post => "\n"} + } + + def self.to_text(html) + html = html.gsub(/[\n\r]/, '').squeeze(' ') + + doc = Loofah.document(html) + doc.scrub!(WikiTags.new(tags)) + doc.scrub!(:newline_block_elements) + + Loofah::Helpers.remove_extraneous_whitespace(doc.text).strip + end + + class WikiTags < ::Loofah::Scrubber + def initialize(tags_to_text) + @direction = :bottom_up + @tags_to_text = tags_to_text || {} + end + + def scrub(node) + formatting = @tags_to_text[node.name] + return CONTINUE unless formatting + node.add_next_sibling Nokogiri::XML::Text.new("#{formatting[:pre]}#{node.content}#{formatting[:post]}", node.document) + node.remove + end + end + end + end +end diff --git a/lib/redmine/wiki_formatting/markdown/html_parser.rb b/lib/redmine/wiki_formatting/markdown/html_parser.rb new file mode 100644 index 000000000..14f89373a --- /dev/null +++ b/lib/redmine/wiki_formatting/markdown/html_parser.rb @@ -0,0 +1,40 @@ +# Redmine - project management software +# Copyright (C) 2006-2015 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module Markdown + class HtmlParser < Redmine::WikiFormatting::HtmlParser + + self.tags = { + 'b' => {:pre => '**', :post => '**'}, + 'strong' => {:pre => '**', :post => '**'}, + 'i' => {:pre => '_', :post => '_'}, + 'em' => {:pre => '_', :post => '_'}, + 'strike' => {:pre => '~~', :post => '~~'}, + 'br' => {:post => "\n"}, + 'h1' => {:pre => "\n\n# ", :post => "\n\n"}, + 'h2' => {:pre => "\n\n## ", :post => "\n\n"}, + 'h3' => {:pre => "\n\n### ", :post => "\n\n"}, + 'h4' => {:pre => "\n\n#### ", :post => "\n\n"}, + 'h5' => {:pre => "\n\n##### ", :post => "\n\n"}, + 'h6' => {:pre => "\n\n###### ", :post => "\n\n"} + } + end + end + end +end diff --git a/lib/redmine/wiki_formatting/textile/html_parser.rb b/lib/redmine/wiki_formatting/textile/html_parser.rb new file mode 100644 index 000000000..201e69c64 --- /dev/null +++ b/lib/redmine/wiki_formatting/textile/html_parser.rb @@ -0,0 +1,41 @@ +# Redmine - project management software +# Copyright (C) 2006-2015 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +module Redmine + module WikiFormatting + module Textile + class HtmlParser < Redmine::WikiFormatting::HtmlParser + + self.tags = { + 'b' => {:pre => '*', :post => '*'}, + 'strong' => {:pre => '*', :post => '*'}, + 'i' => {:pre => '_', :post => '_'}, + 'em' => {:pre => '_', :post => '_'}, + 'u' => {:pre => '+', :post => '+'}, + 'strike' => {:pre => '-', :post => '-'}, + 'br' => {:post => "\n"}, + 'h1' => {:pre => "\n\nh1. ", :post => "\n\n"}, + 'h2' => {:pre => "\n\nh2. ", :post => "\n\n"}, + 'h3' => {:pre => "\n\nh3. ", :post => "\n\n"}, + 'h4' => {:pre => "\n\nh4. ", :post => "\n\n"}, + 'h5' => {:pre => "\n\nh5. ", :post => "\n\n"}, + 'h6' => {:pre => "\n\nh6. ", :post => "\n\n"} + } + end + end + end +end diff --git a/test/fixtures/mail_handler/outlook_2010_html_only.eml b/test/fixtures/mail_handler/outlook_2010_html_only.eml new file mode 100644 index 000000000..0e1428098 --- /dev/null +++ b/test/fixtures/mail_handler/outlook_2010_html_only.eml @@ -0,0 +1,966 @@ +From: jsmith@somenet.foo +To: testuser@example.org +Subject: =?utf-8?Q?Test_email?= +Date: Mon, 11 May 2015 10:50:31 -0500 +MIME-Version: 1.0 +Content-Type: multipart/alternative; + boundary="Mark=_539924359269962179476" +X-Priority: 3 + +This is a multi-part message in MIME format. + +--Mark=_539924359269962179476 +Content-Type: text/plain; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +Simple, unadorned test email generated by Outlook 2010. It is in HTML f= +ormat, but no special formatting has been chosen. I=E2=80=99m going to = +save this as a draft and then manually drop it into the Inbox for scrap= +ing by Redmine 3.0.2. + +--Mark=_539924359269962179476 +Content-Type: text/html; + charset="utf-8" +Content-Transfer-Encoding: quoted-printable + +<STYLE> +pre { +white-space: pre-wrap; /* css-3 */ +white-space: -moz-pre-wrap !important; /* Mozilla, since 1999 */ +white-space: -pre-wrap; /* Opera 4-6 */ +white-space: -o-pre-wrap; /* Opera 7 */ +word-wrap: break-word; /* Internet Explorer 5.5+ */ +} +</STYLE> +<html xmlns:v=3D"urn:schemas-microsoft-com:vml" xmlns:o=3D"urn:schemas-= +microsoft-com:office:office" xmlns:w=3D"urn:schemas-microsoft-com:offic= +e:word" xmlns:m=3D"http://schemas.microsoft.com/office/2004/12/omml" xm= +lns=3D"http://www.w3.org/TR/REC-html40"><head><meta name=3DProgId conte= +nt=3DWord.Document><meta name=3DGenerator content=3D"Microsoft Word 15"= +><meta name=3DOriginator content=3D"Microsoft Word 15"><link rel=3DFile= +-List href=3D"cid:filelist.xml@01D08BD8.4D051580"><!--[if gte mso 9]><x= +ml> +<o:OfficeDocumentSettings> +<o:AllowPNG/> +</o:OfficeDocumentSettings> +</xml><![endif]--><link rel=3DthemeData href=3D"~~themedata~~"><link re= +l=3DcolorSchemeMapping href=3D"~~colorschememapping~~"><!--[if gte mso = +9]><xml> +<w:WordDocument> +<w:Zoom>120</w:Zoom> +<w:SpellingState>Clean</w:SpellingState> +<w:TrackMoves/> +<w:TrackFormatting/> +<w:EnvelopeVis/> +<w:PunctuationKerning/> +<w:ValidateAgainstSchemas/> +<w:SaveIfXMLInvalid>false</w:SaveIfXMLInvalid> +<w:IgnoreMixedContent>false</w:IgnoreMixedContent> +<w:AlwaysShowPlaceholderText>false</w:AlwaysShowPlaceholderText> +<w:DoNotPromoteQF/> +<w:LidThemeOther>EN-US</w:LidThemeOther> +<w:LidThemeAsian>X-NONE</w:LidThemeAsian> +<w:LidThemeComplexScript>X-NONE</w:LidThemeComplexScript> +<w:Compatibility> +<w:DoNotExpandShiftReturn/> +<w:BreakWrappedTables/> +<w:SnapToGridInCell/> +<w:WrapTextWithPunct/> +<w:UseAsianBreakRules/> +<w:DontGrowAutofit/> +<w:SplitPgBreakAndParaMark/> +<w:EnableOpenTypeKerning/> +<w:DontFlipMirrorIndents/> +<w:OverrideTableStyleHps/> +</w:Compatibility> +<w:BrowserLevel>MicrosoftInternetExplorer4</w:BrowserLevel> +<m:mathPr> +<m:mathFont m:val=3D"Cambria Math"/> +<m:brkBin m:val=3D"before"/> +<m:brkBinSub m:val=3D"--"/> +<m:smallFrac m:val=3D"off"/> +<m:dispDef/> +<m:lMargin m:val=3D"0"/> +<m:rMargin m:val=3D"0"/> +<m:defJc m:val=3D"centerGroup"/> +<m:wrapIndent m:val=3D"1440"/> +<m:intLim m:val=3D"subSup"/> +<m:naryLim m:val=3D"undOvr"/> +</m:mathPr></w:WordDocument> +</xml><![endif]--><!--[if gte mso 9]><xml> +<w:LatentStyles DefLockedState=3D"false" DefUnhideWhenUsed=3D"false" De= +fSemiHidden=3D"false" DefQFormat=3D"false" DefPriority=3D"99" LatentSty= +leCount=3D"371"> +<w:LsdException Locked=3D"false" Priority=3D"0" QFormat=3D"true" Name=3D= +"Normal"/> +<w:LsdException Locked=3D"false" Priority=3D"9" QFormat=3D"true" Name=3D= +"heading 1"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 2"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 3"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 4"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 5"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 6"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 7"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 8"/> +<w:LsdException Locked=3D"false" Priority=3D"9" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" QFormat=3D"true" Name=3D"heading 9"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 6"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 7"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 8"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index 9"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 1"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 2"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 3"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 4"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 5"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 6"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 7"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 8"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"toc 9"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Normal Indent"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"footnote text"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"annotation text"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"header"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"footer"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"index heading"/> +<w:LsdException Locked=3D"false" Priority=3D"35" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" QFormat=3D"true" Name=3D"caption"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"table of figures"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"envelope address"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"envelope return"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"footnote reference"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"annotation reference"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"line number"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"page number"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"endnote reference"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"endnote text"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"table of authorities"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"macro"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"toa heading"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Bullet"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Number"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Bullet 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Bullet 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Bullet 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Bullet 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Number 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Number 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Number 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Number 5"/> +<w:LsdException Locked=3D"false" Priority=3D"10" QFormat=3D"true" Name=3D= +"Title"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Closing"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Signature"/> +<w:LsdException Locked=3D"false" Priority=3D"1" SemiHidden=3D"true" Unh= +ideWhenUsed=3D"true" Name=3D"Default Paragraph Font"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text Indent"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Continue"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Continue 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Continue 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Continue 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"List Continue 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Message Header"/> +<w:LsdException Locked=3D"false" Priority=3D"11" QFormat=3D"true" Name=3D= +"Subtitle"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Salutation"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Date"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text First Indent"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text First Indent 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Note Heading"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text Indent 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Body Text Indent 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Block Text"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Hyperlink"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"FollowedHyperlink"/> +<w:LsdException Locked=3D"false" Priority=3D"22" QFormat=3D"true" Name=3D= +"Strong"/> +<w:LsdException Locked=3D"false" Priority=3D"20" QFormat=3D"true" Name=3D= +"Emphasis"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Document Map"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Plain Text"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"E-mail Signature"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Top of Form"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Bottom of Form"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Normal (Web)"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Acronym"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Address"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Cite"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Code"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Definition"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Keyboard"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Preformatted"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Sample"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Typewriter"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"HTML Variable"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Normal Table"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"annotation subject"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"No List"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Outline List 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Outline List 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Outline List 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Simple 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Simple 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Simple 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Classic 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Classic 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Classic 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Classic 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Colorful 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Colorful 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Colorful 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Columns 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Columns 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Columns 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Columns 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Columns 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 6"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 7"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Grid 8"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 4"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 5"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 6"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 7"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table List 8"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table 3D effects 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table 3D effects 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table 3D effects 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Contemporary"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Elegant"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Professional"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Subtle 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Subtle 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Web 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Web 2"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Web 3"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Balloon Text"/> +<w:LsdException Locked=3D"false" Priority=3D"59" Name=3D"Table Grid"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" UnhideWhenUsed=3D"= +true" Name=3D"Table Theme"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" Name=3D"Placeholde= +r Text"/> +<w:LsdException Locked=3D"false" Priority=3D"1" QFormat=3D"true" Name=3D= +"No Spacing"/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading"= +/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1"= +/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2"= +/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1"= +/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2"= +/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3"= +/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List"= +/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid"= +/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List Acc= +ent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid Acc= +ent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1 Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2 Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1 = +Accent 1"/> +<w:LsdException Locked=3D"false" SemiHidden=3D"true" Name=3D"Revision"/= +> +<w:LsdException Locked=3D"false" Priority=3D"34" QFormat=3D"true" Name=3D= +"List Paragraph"/> +<w:LsdException Locked=3D"false" Priority=3D"29" QFormat=3D"true" Name=3D= +"Quote"/> +<w:LsdException Locked=3D"false" Priority=3D"30" QFormat=3D"true" Name=3D= +"Intense Quote"/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2 = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1 = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2 = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3 = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List Acce= +nt 1"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid = +Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List Acc= +ent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid Acc= +ent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1 Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2 Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1 = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2 = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1 = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2 = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3 = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List Acce= +nt 2"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid = +Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List Acc= +ent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid Acc= +ent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1 Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2 Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1 = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2 = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1 = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2 = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3 = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List Acce= +nt 3"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid = +Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List Acc= +ent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid Acc= +ent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1 Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2 Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1 = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2 = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1 = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2 = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3 = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List Acce= +nt 4"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid = +Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List Acc= +ent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid Acc= +ent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1 Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2 Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1 = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2 = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1 = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2 = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3 = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List Acce= +nt 5"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid = +Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"60" Name=3D"Light Shading = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"61" Name=3D"Light List Acc= +ent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"62" Name=3D"Light Grid Acc= +ent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"63" Name=3D"Medium Shading= + 1 Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"64" Name=3D"Medium Shading= + 2 Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"65" Name=3D"Medium List 1 = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"66" Name=3D"Medium List 2 = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"67" Name=3D"Medium Grid 1 = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"68" Name=3D"Medium Grid 2 = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"69" Name=3D"Medium Grid 3 = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"70" Name=3D"Dark List Acce= +nt 6"/> +<w:LsdException Locked=3D"false" Priority=3D"71" Name=3D"Colorful Shadi= +ng Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"72" Name=3D"Colorful List = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"73" Name=3D"Colorful Grid = +Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"19" QFormat=3D"true" Name=3D= +"Subtle Emphasis"/> +<w:LsdException Locked=3D"false" Priority=3D"21" QFormat=3D"true" Name=3D= +"Intense Emphasis"/> +<w:LsdException Locked=3D"false" Priority=3D"31" QFormat=3D"true" Name=3D= +"Subtle Reference"/> +<w:LsdException Locked=3D"false" Priority=3D"32" QFormat=3D"true" Name=3D= +"Intense Reference"/> +<w:LsdException Locked=3D"false" Priority=3D"33" QFormat=3D"true" Name=3D= +"Book Title"/> +<w:LsdException Locked=3D"false" Priority=3D"37" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" Name=3D"Bibliography"/> +<w:LsdException Locked=3D"false" Priority=3D"39" SemiHidden=3D"true" Un= +hideWhenUsed=3D"true" QFormat=3D"true" Name=3D"TOC Heading"/> +<w:LsdException Locked=3D"false" Priority=3D"41" Name=3D"Plain Table 1"= +/> +<w:LsdException Locked=3D"false" Priority=3D"42" Name=3D"Plain Table 2"= +/> +<w:LsdException Locked=3D"false" Priority=3D"43" Name=3D"Plain Table 3"= +/> +<w:LsdException Locked=3D"false" Priority=3D"44" Name=3D"Plain Table 4"= +/> +<w:LsdException Locked=3D"false" Priority=3D"45" Name=3D"Plain Table 5"= +/> +<w:LsdException Locked=3D"false" Priority=3D"40" Name=3D"Grid Table Lig= +ht"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2"/= +> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3"/= +> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4"/= +> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2 A= +ccent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3 A= +ccent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4 A= +ccent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2 A= +ccent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3 A= +ccent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4 A= +ccent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2 A= +ccent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3 A= +ccent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4 A= +ccent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2 A= +ccent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3 A= +ccent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4 A= +ccent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2 A= +ccent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3 A= +ccent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4 A= +ccent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"Grid Table 1 L= +ight Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"Grid Table 2 A= +ccent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"Grid Table 3 A= +ccent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"Grid Table 4 A= +ccent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"Grid Table 5 D= +ark Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"Grid Table 6 C= +olorful Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"Grid Table 7 C= +olorful Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2"/= +> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3"/= +> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4"/= +> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2 A= +ccent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3 A= +ccent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4 A= +ccent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful Accent 1"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2 A= +ccent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3 A= +ccent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4 A= +ccent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful Accent 2"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2 A= +ccent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3 A= +ccent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4 A= +ccent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful Accent 3"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2 A= +ccent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3 A= +ccent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4 A= +ccent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful Accent 4"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2 A= +ccent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3 A= +ccent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4 A= +ccent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful Accent 5"/> +<w:LsdException Locked=3D"false" Priority=3D"46" Name=3D"List Table 1 L= +ight Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"47" Name=3D"List Table 2 A= +ccent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"48" Name=3D"List Table 3 A= +ccent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"49" Name=3D"List Table 4 A= +ccent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"50" Name=3D"List Table 5 D= +ark Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"51" Name=3D"List Table 6 C= +olorful Accent 6"/> +<w:LsdException Locked=3D"false" Priority=3D"52" Name=3D"List Table 7 C= +olorful Accent 6"/> +</w:LatentStyles> +</xml><![endif]--><style><!-- +/* Font Definitions */ +@font-face + {font-family:"Cambria Math"; + panose-1:2 4 5 3 5 4 6 3 2 4; + mso-font-charset:0; + mso-generic-font-family:roman; + mso-font-pitch:variable; + mso-font-signature:-536870145 1107305727 0 0 415 0;} +@font-face + {font-family:Calibri; + panose-1:2 15 5 2 2 2 4 3 2 4; + mso-font-alt:"Times New Roman"; + mso-font-charset:0; + mso-generic-font-family:swiss; + mso-font-pitch:variable; + mso-font-signature:-536870145 1073786111 1 0 415 0;} +/* Style Definitions */ +p.MsoNormal, li.MsoNormal, div.MsoNormal + {mso-style-unhide:no; + mso-style-qformat:yes; + mso-style-parent:""; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:11.0pt; + font-family:"Calibri",sans-serif; + mso-ascii-font-family:Calibri; + mso-ascii-theme-font:minor-latin; + mso-fareast-font-family:Calibri; + mso-fareast-theme-font:minor-latin; + mso-hansi-font-family:Calibri; + mso-hansi-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman"; + mso-bidi-theme-font:minor-bidi;} +a:link, span.MsoHyperlink + {mso-style-noshow:yes; + mso-style-priority:99; + color:blue; + mso-themecolor:hyperlink; + text-decoration:underline; + text-underline:single;} +a:visited, span.MsoHyperlinkFollowed + {mso-style-noshow:yes; + mso-style-priority:99; + color:purple; + mso-themecolor:followedhyperlink; + text-decoration:underline; + text-underline:single;} +p.MsoPlainText, li.MsoPlainText, div.MsoPlainText + {mso-style-noshow:yes; + mso-style-priority:99; + mso-style-link:"Plain Text Char"; + margin:0in; + margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:11.0pt; + mso-bidi-font-size:10.5pt; + font-family:"Calibri",sans-serif; + mso-fareast-font-family:Calibri; + mso-fareast-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman"; + mso-bidi-theme-font:minor-bidi;} +span.PlainTextChar + {mso-style-name:"Plain Text Char"; + mso-style-noshow:yes; + mso-style-priority:99; + mso-style-unhide:no; + mso-style-locked:yes; + mso-style-link:"Plain Text"; + mso-bidi-font-size:10.5pt; + font-family:"Calibri",sans-serif; + mso-ascii-font-family:Calibri; + mso-hansi-font-family:Calibri;} +span.EmailStyle19 + {mso-style-type:personal-compose; + mso-style-noshow:yes; + mso-style-unhide:no;} +.MsoChpDefault + {mso-style-type:export-only; + mso-default-props:yes; + font-size:10.0pt; + mso-ansi-font-size:10.0pt; + mso-bidi-font-size:10.0pt; + font-family:"Calibri",sans-serif; + mso-ascii-font-family:Calibri; + mso-ascii-theme-font:minor-latin; + mso-fareast-font-family:Calibri; + mso-fareast-theme-font:minor-latin; + mso-hansi-font-family:Calibri; + mso-hansi-theme-font:minor-latin; + mso-bidi-font-family:"Times New Roman"; + mso-bidi-theme-font:minor-bidi;} +@page WordSection1 + {size:8.5in 11.0in; + margin:1.0in 1.0in 1.0in 1.0in; + mso-header-margin:.5in; + mso-footer-margin:.5in; + mso-paper-source:0;} +div.WordSection1 + {page:WordSection1;} +--></style><!--[if gte mso 10]><style>/* Style Definitions */ +table.MsoNormalTable + {mso-style-name:"Table Normal"; + mso-tstyle-rowband-size:0; + mso-tstyle-colband-size:0; + mso-style-noshow:yes; + mso-style-priority:99; + mso-style-parent:""; + mso-padding-alt:0in 5.4pt 0in 5.4pt; + mso-para-margin:0in; + mso-para-margin-bottom:.0001pt; + mso-pagination:widow-orphan; + font-size:10.0pt; + font-family:"Calibri",sans-serif; + mso-ascii-font-family:Calibri; + mso-ascii-theme-font:minor-latin; + mso-hansi-font-family:Calibri; + mso-hansi-theme-font:minor-latin;} +</style><![endif]--><!--[if gte mso 9]><xml> +<o:shapedefaults v:ext=3D"edit" spidmax=3D"1026" /> +</xml><![endif]--><!--[if gte mso 9]><xml> +<o:shapelayout v:ext=3D"edit"> +<o:idmap v:ext=3D"edit" data=3D"1" /> +</o:shapelayout></xml><![endif]--></head><body lang=3DEN-US link=3Dblue= + vlink=3Dpurple style=3D'tab-interval:.5in'><div class=3DWordSection1><= +p class=3DMsoPlainText>Simple, unadorned test email generated by Outloo= +k 2010. It is in HTML format, but no special formatting has been chosen= +. I=E2=80=99m going to save this as a draft and then manually drop it i= +nto the Inbox for scraping by Redmine 3.0.2.<o:p></o:p></p></div></body= +></html> + +--Mark=_539924359269962179476-- diff --git a/test/fixtures/mail_handler/outlook_web_access_2010_html_only.eml b/test/fixtures/mail_handler/outlook_web_access_2010_html_only.eml new file mode 100644 index 000000000..b2f6a566f --- /dev/null +++ b/test/fixtures/mail_handler/outlook_web_access_2010_html_only.eml @@ -0,0 +1,65 @@ +From: "John Smith" <jsmith@somenet.foo> +To: redmine <r@MYCOMPANYNAME.com> +Subject: Upgrade Redmine to 3.0.x +Thread-Topic: Upgrade Redmine to 3.0.x +Thread-Index: AQHQknBe94y5Or7Yl02JransMRF41p2Dv6Hu +Date: Tue, 19 May 2015 16:27:43 -0400 +Message-ID: <A2D341A8808F024CAFA63F1287B9929CF1BC440F@EMBX01.exch.local> +Accept-Language: en-US +Content-Language: en-US +X-MS-Exchange-Organization-AuthAs: Internal +X-MS-Exchange-Organization-AuthMechanism: 04 +X-MS-Exchange-Organization-AuthSource: EHUB01.exch.local +X-MS-Has-Attach: +X-MS-Exchange-Organization-SCL: -1 +X-MS-TNEF-Correlator: +Content-Type: text/html; charset="iso-8859-1" +Content-Transfer-Encoding: quoted-printable +MIME-Version: 1.0 + +<html dir=3D"ltr"> +<head> +<meta http-equiv=3D"Content-Type" content=3D"text/html; charset=3Diso-8859-= +1"> +<style>=0A= +<!--=0A= +body=0A= + {font-family:Verdana,sans-serif;=0A= + font-size:0.8em;=0A= + color:#484848}=0A= +h1, h2, h3=0A= + {font-family:"Trebuchet MS",Verdana,sans-serif;=0A= + margin:0px}=0A= +h1=0A= + {font-size:1.2em}=0A= +h2, h3=0A= + {font-size:1.1em}=0A= +a, a:link, a:visited=0A= + {color:#2A5685}=0A= +a:hover, a:active=0A= + {color:#c61a1a}=0A= +fieldset.attachments=0A= + {border-width:1px 0 0 0}=0A= +hr=0A= + {width:100%;=0A= + height:1px;=0A= + background:#ccc;=0A= + border:0}=0A= +span.footer=0A= + {font-size:0.8em;=0A= + font-style:italic}=0A= +-->=0A= +</style><style id=3D"owaParaStyle" type=3D"text/css">P {margin-top:0;margin= +-bottom:0;}</style> +</head> +<body ocsi=3D"0" fpstyle=3D"1"> +<div style=3D"direction: ltr;font-family: Tahoma;color: #000000;font-size: = +10pt;">A mess.<br> +<div><br> +<div style=3D"font-family:Tahoma; font-size:13px">--Geoff Maciolek<br> +MYCOMPANYNAME, LLC<br> +</div> +</div> +</div> +</body> +</html>
\ No newline at end of file diff --git a/test/fixtures/mail_handler/ticket_html_only.eml b/test/fixtures/mail_handler/ticket_html_only.eml index 511e5f107..83710bd65 100644 --- a/test/fixtures/mail_handler/ticket_html_only.eml +++ b/test/fixtures/mail_handler/ticket_html_only.eml @@ -15,8 +15,9 @@ Content-Transfer-Encoding: 7bit <!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"> <html> <head> +<style>p {font-size:12.0pt;}</style> </head> <body bgcolor="#ffffff" text="#000000"> -This is a <b>html-only</b> email.<br> +This is a <b>html-only</b> email.<br><h1>With a title</h1><p>and a paragraph.</p> </body> </html> diff --git a/test/unit/lib/redmine/wiki_formatting/html_parser_test.rb b/test/unit/lib/redmine/wiki_formatting/html_parser_test.rb new file mode 100644 index 000000000..4662aeaeb --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/html_parser_test.rb @@ -0,0 +1,30 @@ +# Redmine - project management software +# Copyright (C) 2006-2015 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../test_helper', __FILE__) + +class Redmine::WikiFormatting::HtmlParserTest < ActiveSupport::TestCase + + def setup + @parser = Redmine::WikiFormatting::HtmlParser + end + + def test_convert_line_breaks + assert_equal "A html snippet with\na new line.", + @parser.to_text('<p>A html snippet with<br>a new line.</p>') + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/markdown_html_parser_test.rb b/test/unit/lib/redmine/wiki_formatting/markdown_html_parser_test.rb new file mode 100644 index 000000000..01e248021 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/markdown_html_parser_test.rb @@ -0,0 +1,30 @@ +# Redmine - project management software +# Copyright (C) 2006-2015 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../test_helper', __FILE__) + +class Redmine::WikiFormatting::MarkdownHtmlParserTest < ActiveSupport::TestCase + + def setup + @parser = Redmine::WikiFormatting::Markdown::HtmlParser + end + + def test_should_convert_tags + assert_equal 'A **simple** html snippet.', + @parser.to_text('<p>A <b>simple</b> html snippet.</p>') + end +end diff --git a/test/unit/lib/redmine/wiki_formatting/textile_html_parser_test.rb b/test/unit/lib/redmine/wiki_formatting/textile_html_parser_test.rb new file mode 100644 index 000000000..cc838bbf0 --- /dev/null +++ b/test/unit/lib/redmine/wiki_formatting/textile_html_parser_test.rb @@ -0,0 +1,30 @@ +# Redmine - project management software +# Copyright (C) 2006-2015 Jean-Philippe Lang +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +require File.expand_path('../../../../../test_helper', __FILE__) + +class Redmine::WikiFormatting::TextileHtmlParserTest < ActiveSupport::TestCase + + def setup + @parser = Redmine::WikiFormatting::Textile::HtmlParser + end + + def test_should_convert_tags + assert_equal 'A *simple* html snippet.', + @parser.to_text('<p>A <b>simple</b> html snippet.</p>') + end +end diff --git a/test/unit/mail_handler_test.rb b/test/unit/mail_handler_test.rb index 99900666d..4fc710ba2 100644 --- a/test/unit/mail_handler_test.rb +++ b/test/unit/mail_handler_test.rb @@ -797,13 +797,33 @@ class MailHandlerTest < ActiveSupport::TestCase assert_equal Message.find(1), m.parent end - def test_should_strip_tags_of_html_only_emails - issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'}) + def test_should_convert_tags_of_html_only_emails + with_settings :text_formatting => 'textile' do + issue = submit_email('ticket_html_only.eml', :issue => {:project => 'ecookbook'}) + assert issue.is_a?(Issue) + assert !issue.new_record? + issue.reload + assert_equal 'HTML email', issue.subject + assert_equal "This is a *html-only* email.\r\n\r\nh1. With a title\r\n\r\nand a paragraph.", issue.description + end + end + + def test_should_handle_outlook_web_access_2010_html_only + issue = submit_email('outlook_web_access_2010_html_only.eml', :issue => {:project => 'ecookbook'}) + assert issue.is_a?(Issue) + issue.reload + assert_equal 'Upgrade Redmine to 3.0.x', issue.subject + assert_equal "A mess.\r\n\r\n--Geoff Maciolek\r\nMYCOMPANYNAME, LLC", issue.description + end + + def test_should_handle_outlook_2010_html_only + issue = submit_email('outlook_2010_html_only.eml', :issue => {:project => 'ecookbook'}) assert issue.is_a?(Issue) - assert !issue.new_record? issue.reload - assert_equal 'HTML email', issue.subject - assert_equal 'This is a html-only email.', issue.description + assert_equal 'Test email', issue.subject + assert_equal "Simple, unadorned test email generated by Outlook 2010. It is in HTML format, but" + + " no special formatting has been chosen. I’m going to save this as a draft and then manually" + + " drop it into the Inbox for scraping by Redmine 3.0.2.", issue.description end test "truncate emails with no setting should add the entire email into the issue" do |