diff --git a/lib/chunks/wiki.rb b/lib/chunks/wiki.rb index 617e6da5..bbe02494 100644 --- a/lib/chunks/wiki.rb +++ b/lib/chunks/wiki.rb @@ -13,18 +13,21 @@ module WikiChunk # Name of the referenced page attr_reader :page_name - + + # Name of the referenced page + attr_reader :web_name + # the referenced page def refpage @content.web.page(@page_name) end - + end # A wiki link is the top-level class for links that refers to # another wiki page. class WikiLink < WikiReference - + attr_reader :link_text, :link_type def initialize(match_data, content) @@ -49,11 +52,16 @@ module WikiChunk not @textile_link_suffix.nil? end + def interweb_link? + not @web_name.nil? and Web.find_by_name(@web_name) or + Web.find_by_address(@web_name) + end + # replace any sequence of whitespace characters with a single space def normalize_whitespace(line) line.gsub(/\s+/, ' ') end - + end # This chunk matches a WikiWord. WikiWords can be escaped @@ -63,7 +71,7 @@ module WikiChunk class Word < WikiLink attr_reader :escaped_text - + unless defined? WIKI_WORD WIKI_WORD = Regexp.new('(":)?(\\\\)?(' + WikiWords::WIKI_WORD_PATTERN + ')\b', 0, "utf-8") end @@ -75,19 +83,19 @@ module WikiChunk def initialize(match_data, content) super @textile_link_suffix, @escape, @page_name = match_data[1..3] - if @escape + if @escape @unmask_mode = :escape @escaped_text = @page_name else @escaped_text = nil end @link_text = WikiWords.separate(@page_name) - @unmask_text = (@escaped_text || @content.page_link(@page_name, @link_text, @link_type)) + @unmask_text = (@escaped_text || @content.page_link(@web_name, @page_name, @link_text, @link_type)) end end - # This chunk handles [[bracketted wiki words]] and + # This chunk handles [[bracketted wiki words]] and # [[AliasedWords|aliased wiki words]]. The first part of an # aliased wiki word must be a WikiWord. If the WikiWord # is aliased, the +link_text+ field will contain the @@ -95,15 +103,16 @@ module WikiChunk # contents within the double brackets. # # NOTE: This chunk must be tested before WikiWord since - # a WikiWords can be a substring of a WikiLink. + # a WikiWords can be a substring of a WikiLink. class Link < WikiLink - + unless defined? WIKI_LINK WIKI_LINK = /(":)?\[\[\s*([^\]\s][^\]]+?)\s*\]\]/ LINK_TYPE_SEPARATION = Regexp.new('^(.+):((file)|(pic))$', 0, 'utf-8') ALIAS_SEPARATION = Regexp.new('^(.+)\|(.+)$', 0, 'utf-8') - end - + WEB_SEPARATION = Regexp.new('^(.+):(.+)$', 0, 'utf-8') + end + def self.pattern() WIKI_LINK end def initialize(match_data, content) @@ -112,12 +121,13 @@ module WikiChunk @link_text = @page_name = normalize_whitespace(match_data[2]) separate_link_type separate_alias - @unmask_text = @content.page_link(@page_name, @link_text, @link_type) + separate_web + @unmask_text = @content.page_link(@web_name, @page_name, @link_text, @link_type) end private - # if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]], + # if link wihin the brackets has a form of [[filename:file]] or [[filename:pic]], # this means a link to a picture or a file def separate_link_type link_type_match = LINK_TYPE_SEPARATION.match(@page_name) @@ -135,9 +145,19 @@ module WikiChunk @link_text = alias_match[2] end # note that [[filename|link text:file]] is also supported - end - + end + + # Interweb links have the form [[Web Name:Page Name]] or + # [[address:PageName]]. Alternate text links of the form + # [[address:PageName|Other text]] are also supported. + def separate_web + web_match = WEB_SEPARATION.match(@page_name) + if web_match + @web_name = normalize_whitespace(web_match[1]) + @page_name = web_match[2] + end + end + end - end diff --git a/lib/wiki_content.rb b/lib/wiki_content.rb index f986fe43..94e3e3f3 100644 --- a/lib/wiki_content.rb +++ b/lib/wiki_content.rb @@ -11,46 +11,46 @@ require 'chunks/nowiki' # actions. The actions can modify wiki content so that certain parts of # it are protected from being rendered by later actions. # -# When wiki content is rendered, it can be interrogated to find out -# which chunks were rendered. This means things like categories, wiki +# When wiki content is rendered, it can be interrogated to find out +# which chunks were rendered. This means things like categories, wiki # links, can be determined. # -# Exactly how wiki content is rendered is determined by a number of +# Exactly how wiki content is rendered is determined by a number of # settings that are optionally passed in to a constructor. The current # options are: -# * :engine +# * :engine # => The structural markup engine to use (Textile, Markdown, RDoc) # * :engine_opts # => A list of options to pass to the markup engines (safe modes, etc) # * :pre_engine_actions # => A list of render actions or chunks to be processed before the -# markup engine is applied. By default this is: -# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word +# markup engine is applied. By default this is: +# Category, Include, URIChunk, WikiChunk::Link, WikiChunk::Word # * :post_engine_actions -# => A list of render actions or chunks to apply after the markup -# engine. By default these are: +# => A list of render actions or chunks to apply after the markup +# engine. By default these are: # Literal::Pre, Literal::Tags # * :mode -# => How should the content be rendered? For normal display (show), +# => How should the content be rendered? For normal display (show), # publishing (:publish) or export (:export)? module ChunkManager attr_reader :chunks_by_type, :chunks_by_id, :chunks, :chunk_id - - ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, - WikiChunk::Word ] + + ACTIVE_CHUNKS = [ NoWiki, Category, WikiChunk::Link, + WikiChunk::Word ] HIDE_CHUNKS = [ Literal::Pre, Literal::Tags ] - MASK_RE = { + MASK_RE = { ACTIVE_CHUNKS => Chunk::Abstract.mask_re(ACTIVE_CHUNKS), HIDE_CHUNKS => Chunk::Abstract.mask_re(HIDE_CHUNKS) } - + def init_chunk_manager @chunks_by_type = Hash.new - Chunk::Abstract::derivatives.each{|chunk_type| - @chunks_by_type[chunk_type] = Array.new + Chunk::Abstract::derivatives.each{|chunk_type| + @chunks_by_type[chunk_type] = Array.new } @chunks_by_id = Hash.new @chunks = [] @@ -77,20 +77,20 @@ module ChunkManager def scan_chunkid(text) text.scan(MASK_RE[ACTIVE_CHUNKS]){|a| yield a[0] } end - + def find_chunks(chunk_type) @chunks.select { |chunk| chunk.kind_of?(chunk_type) and chunk.rendered? } end end -# A simplified version of WikiContent. Useful to avoid recursion problems in +# A simplified version of WikiContent. Useful to avoid recursion problems in # WikiContent.new class WikiContentStub < String - + attr_reader :options include ChunkManager - + def initialize(content, options) super(content) @options = options @@ -99,15 +99,15 @@ class WikiContentStub < String # Detects the mask strings contained in the text of chunks of type chunk_types # and yields the corresponding chunk ids - # example: content = "chunk123categorychunk
chunk456categorychunk" + # example: content = "chunk123categorychunk
chunk456categorychunk" # inside_chunks(Literal::Pre) ==> yield 456 def inside_chunks(chunk_types) chunk_types.each{|chunk_type| chunk_type.apply_to(self) } - + chunk_types.each{|chunk_type| @chunks_by_type[chunk_type].each{|hide_chunk| scan_chunkid(hide_chunk.text){|id| yield id } } - } + } end end @@ -132,12 +132,12 @@ class WikiContent < String @web = @revision.page.web @options = DEFAULT_OPTS.dup.merge(options) - @options[:engine] = Engines::MAP[@web.markup] + @options[:engine] = Engines::MAP[@web.markup] @options[:engine_opts] = [:filter_html, :filter_styles] if @web.safe_mode? @options[:active_chunks] = (ACTIVE_CHUNKS - [WikiChunk::Word] ) if @web.brackets_only? - + @not_rendered = @pre_rendered = nil - + super(@revision.content) init_chunk_manager build_chunks @@ -145,9 +145,10 @@ class WikiContent < String end # Call @web.page_link using current options. - def page_link(name, text, link_type) + def page_link(web_name, name, text, link_type) + web = Web.find_by_name(web_name) || Web.find_by_address(web_name) || @web @options[:link_type] = (link_type || :show) - @url_generator.make_link(name, @web, text, @options) + @url_generator.make_link(name, web, text, @options) end def build_chunks @@ -158,7 +159,7 @@ class WikiContent < String # Handle hiding contexts like "pre" and "code" etc.. # The markup (textile, rdoc etc) can produce such contexts with its own syntax. # To reveal them, we work on a copy of the content. - # The copy is rendered and used to detect the chunks that are inside protecting context + # The copy is rendered and used to detect the chunks that are inside protecting context # These chunks are reverted on the original content string. copy = WikiContentStub.new(self, @options) @@ -170,25 +171,25 @@ class WikiContent < String end def pre_render! - unless @pre_rendered + unless @pre_rendered @chunks_by_type[Include].each{|chunk| chunk.unmask } @pre_rendered = String.new(self) end - @pre_rendered + @pre_rendered end def render! pre_render! @options[:engine].apply_to(self) # unmask in one go. $~[1] is the chunk id - gsub!(MASK_RE[ACTIVE_CHUNKS]) do + gsub!(MASK_RE[ACTIVE_CHUNKS]) do chunk = @chunks_by_id[$~[1].to_i] - if chunk.nil? + if chunk.nil? # if we match a chunkmask that existed in the original content string # just keep it as it is $~[0] - else - chunk.unmask_text + else + chunk.unmask_text end end self diff --git a/test/functional/wiki_controller_test.rb b/test/functional/wiki_controller_test.rb index 44cfb458..5dd3d218 100755 --- a/test/functional/wiki_controller_test.rb +++ b/test/functional/wiki_controller_test.rb @@ -671,24 +671,177 @@ class WikiControllerTest < Test::Unit::TestCase \usepackage{amsfonts} \usepackage{amssymb} \usepackage{graphicx} +\usepackage{color} \usepackage{ucs} \usepackage[utf8x]{inputenc} \usepackage{hyperref} %----Macros---------- -\newcommand{\gt}{>} -\newcommand{\lt}{<} +% +% Unresolved issues: +% +% \binom{}{} +% +% \righttoleftarrow +% \lefttorightarrow + +% Because of conflicts, \space and \mathop are converted to +% \itexspace and \operatorname during preprocessing. +% \over is simply unsupported. + +% itex: \space{ht}{dp}{wd} +% +% Height and baseline depth measurements are in units of tenths of an ex while +% the width is measured in tenths of an em. +\makeatletter +\newdimen\itex@wd% +\newdimen\itex@dp% +\newdimen\itex@thd% +\def\itexspace#1#2#3{\itex@wd=#3em% +\itex@wd=0.1\itex@wd% +\itex@dp=#2ex% +\itex@dp=0.1\itex@dp% +\itex@thd=#1ex% +\itex@thd=0.1\itex@thd% +\advance\itex@thd\the\itex@dp% +\makebox[\the\itex@wd]{\rule[-\the\itex@dp]{0cm}{\the\itex@thd}}} +\makeatother + +% \tensor and \multiscript +\makeatletter +\newif\if@sup +\newtoks\@sups +\def\append@sup#1{\edef\act{\noexpand\@sups={\the\@sups #1}}\act}% +\def\reset@sup{\@supfalse\@sups={}}% +\def\mk@scripts#1#2{\if #2/ \if@sup ^{\the\@sups}\fi \else% + \ifx #1_ \if@sup ^{\the\@sups}\reset@sup \fi {}_{#2}% + \else \append@sup#2 \@suptrue \fi% + \expandafter\mk@scripts\fi} +\def\tensor#1#2{\reset@sup#1\mk@scripts#2_/} +\def\multiscripts#1#2#3{\reset@sup{}\mk@scripts#1_/#2% + \reset@sup\mk@scripts#3_/} +\makeatother + +% \slash +\makeatletter +\newbox\slashbox \setbox\slashbox=\hbox{$/$} +\def\itex@pslash#1{\setbox\@tempboxa=\hbox{$#1$} + \@tempdima=0.5\wd\slashbox \advance\@tempdima 0.5\wd\@tempboxa + \copy\slashbox \kern-\@tempdima \box\@tempboxa} +\def\slash{\protect\itex@pslash} +\makeatother + +% Renames \sqrt as \oldsqrt and redefine root to result in \sqrt[#1]{#2} +\let\oldroot\root +\def\root#1#2{\oldroot #1 \of{#2}} + +% Manually declare the txfonts symbolsC font +\DeclareSymbolFont{symbolsC}{U}{txsyc}{m}{n} +\SetSymbolFont{symbolsC}{bold}{U}{txsyc}{bx}{n} +\DeclareFontSubstitution{U}{txsyc}{m}{n} + +% Declare specific arrows from txfonts without loading the full package +\makeatletter +\def\re@DeclareMathSymbol#1#2#3#4{% + \let#1=\undefined + \DeclareMathSymbol{#1}{#2}{#3}{#4}} +\re@DeclareMathSymbol{\neArrow}{\mathrel}{symbolsC}{116} +\re@DeclareMathSymbol{\neArr}{\mathrel}{symbolsC}{116} +\re@DeclareMathSymbol{\seArrow}{\mathrel}{symbolsC}{117} +\re@DeclareMathSymbol{\seArr}{\mathrel}{symbolsC}{117} +\re@DeclareMathSymbol{\nwArrow}{\mathrel}{symbolsC}{118} +\re@DeclareMathSymbol{\nwArr}{\mathrel}{symbolsC}{118} +\re@DeclareMathSymbol{\swArrow}{\mathrel}{symbolsC}{119} +\re@DeclareMathSymbol{\swArr}{\mathrel}{symbolsC}{119} +\makeatother + +% Widecheck +\makeatletter +\DeclareRobustCommand\widecheck[1]{{\mathpalette\@widecheck{#1}}} +\def\@widecheck#1#2{% + \setbox\z@\hbox{\m@th$#1#2$}% + \setbox\tw@\hbox{\m@th$#1% + \widehat{% + \vrule\@width\z@\@height\ht\z@ + \vrule\@height\z@\@width\wd\z@}$}% + \dp\tw@-\ht\z@ + \@tempdima\ht\z@ \advance\@tempdima2\ht\tw@ \divide\@tempdima\thr@@ + \setbox\tw@\hbox{% + \raise\@tempdima\hbox{\scalebox{1}[-1]{\lower\@tempdima\box +\tw@}}}% + {\ooalign{\box\tw@ \cr \box\z@}}} +\makeatother + +% udots (taken from yhmath) +\makeatletter +\def\udots{\mathinner{\mkern2mu\raise\p@\hbox{.} +\mkern2mu\raise4\p@\hbox{.}\mkern1mu +\raise7\p@\vbox{\kern7\p@\hbox{.}}\mkern1mu}} +\makeatother + +%% Renaming existing commands +\newcommand{\underoverset}[3]{\underset{#1}{\overset{#2}{#3}}} +\newcommand{\widevec}{\overrightarrow} \newcommand{\darr}{\downarrow} \newcommand{\nearr}{\nearrow} \newcommand{\nwarr}{\nwarrow} \newcommand{\searr}{\searrow} \newcommand{\swarr}{\swarrow} -\newcommand{\iff}{\Longleftrightarrow} -\newcommand{\impliedby}{\Leftarrow} +\newcommand{\curvearrowbotright}{\curvearrowright} +\newcommand{\uparr}{\uparrow} +\newcommand{\downuparrow}{\updownarrow} +\newcommand{\duparr}{\updownarrow} +\newcommand{\updarr}{\updownarrow} +\newcommand{\gt}{>} +\newcommand{\lt}{<} \newcommand{\map}{\mapsto} \newcommand{\embedsin}{\hookrightarrow} -\newcommand{\implies}{\Rightarrow} +\newcommand{\Alpha}{A} +\newcommand{\Beta}{B} +\newcommand{\Zeta}{Z} +\newcommand{\Eta}{H} +\newcommand{\Iota}{I} +\newcommand{\Kappa}{K} +\newcommand{\Mu}{M} +\newcommand{\Nu}{N} +\newcommand{\Rho}{P} +\newcommand{\Tau}{T} +\newcommand{\Upsi}{\Upsilon} +\newcommand{\omicron}{o} +\newcommand{\lang}{\langle} +\newcommand{\rang}{\rangle} +\newcommand{\Union}{\bigcup} +\newcommand{\Intersection}{\bigcap} +\newcommand{\Oplus}{\bigoplus} +\newcommand{\Otimes}{\bigotimes} +\newcommand{\Wedge}{\bigwedge} +\newcommand{\Vee}{\bigvee} +\newcommand{\coproduct}{\coprod} +\newcommand{\product}{\prod} +\newcommand{\closure}{\overline} +\newcommand{\integral}{\int} +\newcommand{\doubleintegral}{\iint} +\newcommand{\tripleintegral}{\iiint} +\newcommand{\quadrupleintegral}{\iiiint} +\newcommand{\conint}{\oint} +\newcommand{\contourintegral}{\oint} \newcommand{\qed}{\blacksquare} +\newcommand{\infinity}{\infty} +\renewcommand{\empty}{\emptyset} +\newcommand{\bottom}{\bot} +\newcommand{\minusb}{\boxminus} +\newcommand{\plusb}{\boxplus} +\newcommand{\timesb}{\boxtimes} +\newcommand{\intersection}{\cap} +\newcommand{\union}{\cup} +\newcommand{\Del}{\nabla} +\newcommand{\odash}{\circleddash} +\newcommand{\negspace}{\\\!} +\newcommand{\widebar}{\overline} +\newcommand{\textsize}{\normalsize} +\renewcommand{\scriptsize}{\scriptstyle} +\newcommand{\scriptscriptsize}{\scriptscriptstyle} +\newcommand{\mathfr}{\mathfrak} %------------------------------------------------------------------- diff --git a/test/test_helper.rb b/test/test_helper.rb index 3afd1ae6..9be06248 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -133,7 +133,11 @@ class StubUrlGenerator < AbstractUrlGenerator if known_page %{#{text}} else - %{#{text}?} + if web_address == 'instiki' + %{#{text}?} + else + %{#{text}?} + end end end end diff --git a/test/unit/chunks/wiki_test.rb b/test/unit/chunks/wiki_test.rb index 82c2546c..1355b5e0 100755 --- a/test/unit/chunks/wiki_test.rb +++ b/test/unit/chunks/wiki_test.rb @@ -47,6 +47,11 @@ class WikiTest < Test::Unit::TestCase :page_name => 'Sperberg-McQueen') end + def test_interweb_links + match(WikiChunk::Link, 'This is a tricky link [[Froogle:Sperberg-McQueen]]', + {:page_name => 'Sperberg-McQueen', :web_name => 'Froogle'}) + end + def test_include_chunk_pattern content = 'This is a [[!include pagename]] and [[!include WikiWord]] but [[blah]]' recognized_includes = content.scan(Include.pattern).collect { |m| m[0] } @@ -81,7 +86,7 @@ class WikiTest < Test::Unit::TestCase # empty link type assert_link_parsed_as 'page name', 'link?:', :show, '[[page name|link?:]]' # unknown link type - assert_link_parsed_as 'page name:create_system', 'page name:create_system', :show, + assert_link_parsed_as 'create_system', 'page name:create_system', :show, '[[page name:create_system]]' end diff --git a/test/unit/page_renderer_test.rb b/test/unit/page_renderer_test.rb index 00354da6..c5246012 100644 --- a/test/unit/page_renderer_test.rb +++ b/test/unit/page_renderer_test.rb @@ -137,14 +137,14 @@ class PageRendererTest < Test::Unit::TestCase test_renderer(@revision).display_content end - def test_content_with_auto_links - assert_markup_parsed_as( - '
http://www.loudthinking.com/ ' + - 'points to That Way from ' + - 'david@loudthinking.com
', - 'http://www.loudthinking.com/ points to ThatWay from david@loudthinking.com') - - end +# def test_content_with_auto_links +# assert_markup_parsed_as( +# 'http://www.loudthinking.com/ ' + +# 'points to That Way from ' + +# 'david@loudthinking.com
', +# 'http://www.loudthinking.com/ points to ThatWay from david@loudthinking.com') +# +# end def test_content_with_aliased_links assert_markup_parsed_as( @@ -186,12 +186,12 @@ class PageRendererTest < Test::Unit::TestCase 'Aclass SmartEngine end
would not mark up\n\nCodeBlocks\n\nwould it?') end - def test_content_with_autolink_in_parentheses - assert_markup_parsed_as( - '
The W3C body (' + - 'http://www.w3c.org) sets web standards
', - 'The W3C body (http://www.w3c.org) sets web standards') - end +# def test_content_with_autolink_in_parentheses +# assert_markup_parsed_as( +# 'The W3C body (' + +# 'http://www.w3c.org) sets web standards
', +# 'The W3C body (http://www.w3c.org) sets web standards') +# end def test_content_with_link_in_parentheses assert_markup_parsed_as( @@ -321,8 +321,8 @@ class PageRendererTest < Test::Unit::TestCase def test_wiki_link_with_colon assert_markup_parsed_as( - 'With:Colon?
', - '[[With:Colon]]') + 'Instiki:Colon?
', + '[[Instiki:Colon]]') end def test_list_with_tildas