#!/usr/bin/env ruby # Uncomment the line below to enable pdflatex tests; don't forget to comment them again # commiting to SVN # $INSTIKI_TEST_PDFLATEX = true require File.expand_path(File.dirname(__FILE__) + '/../test_helper') require 'wiki_controller' require 'rexml/document' require 'tempfile' require 'zip/zipfilesystem' require 'stringsupport' # Raise errors beyond the default web-based presentation class WikiController; def rescue_action(e) logger.error(e); raise e end; end class WikiControllerTest < ActionController::TestCase fixtures :webs, :pages, :revisions, :system, :wiki_references def setup @controller = WikiController.new @controller.extend ApplicationHelper @request = ActionController::TestRequest.new @response = ActionController::TestResponse.new class << @request.session attr_accessor :dbman end # simulate a cookie session store @request.session.dbman = FakeSessionDbMan @wiki = Wiki.new @web = webs(:test_wiki) @home = @page = pages(:home_page) @oak = pages(:oak) @liquor = pages(:liquor) @elephant = pages(:elephant) @eternity = Regexp.new('author=.*; path=/; expires=' + Time.utc(2030).strftime("%a, %d-%b-%Y %H:%M:%S GMT")) set_tex_header end def test_truncate_page_name wanted_page_name = 'This is a very, very, very, very, VERY long page name' evil_page_name = 'This page has plenty of fun & games' unicode_page_name = "This p\xF0\x9D\x94\xB8\xF0\x9D\x94\xBE\xF0\x9D\x94\xBC has plenty of fun & games" assert_equal 'This is a very, very, very,...', @controller.truncate(WikiWords.separate(wanted_page_name), :length => 35) assert_equal 'This page has plenty of fun...', @controller.truncate(WikiWords.separate(evil_page_name)) truncated = "".respond_to?(:force_encoding) ? "This p\u{1D538}\u{1D53E}\u{1D53C} has plenty of fun\u2633\u2633" : "This p\xF0\x9D\x94\xB8\xF0\x9D\x94\xBE\xF0\x9D\x94\xBC has plenty of fun\xE2\x98\xB3\xE2\x98\xB3" assert_equal truncated, @controller.truncate(WikiWords.separate(unicode_page_name), :omission =>"\xE2\x98\xB3\xE2\x98\xB3") end def test_authenticate set_web_property :password, 'pswd' get :authenticate, :web => 'wiki1', :password => 'pswd' assert_redirected_to :web => 'wiki1', :controller => 'wiki', :action => 'show', :id => 'HomePage' assert_equal 'pswd', @response.cookies['wiki1'] end def test_authenticate_wrong_password set_web_property :password, 'pswd' r = process('authenticate', 'web' => 'wiki1', 'password' => 'wrong password') assert_redirected_to :action => 'login', :controller => 'wiki', :web => 'wiki1' assert_nil r.cookies['web_address'] end def test_authors @wiki.write_page('wiki1', 'BreakSortingOrder', "This page breaks the accidentally correct sorting order of authors", Time.now, Author.new('BreakingTheOrder', '127.0.0.2'), x_test_renderer) r = process('authors', 'web' => 'wiki1') assert_response(:success) assert_equal %w(AnAuthor BreakingTheOrder DavidHeinemeierHansson Guest Me TreeHugger), r.template_objects['authors'] page_names_by_author = r.template_objects['page_names_by_author'] assert_equal r.template_objects['authors'], page_names_by_author.keys.sort assert_equal %w(FirstPage HomePage), page_names_by_author['DavidHeinemeierHansson'] end def test_cancel_edit @oak.lock(Time.now, 'Locky') assert @oak.locked?(Time.now) r = process('cancel_edit', 'web' => 'wiki1', 'id' => 'Oak') assert_redirected_to :web => 'wiki1', :controller => 'wiki', :action => 'show', :id => 'Oak' assert !Page.find(@oak.id).locked?(Time.now) end def test_edit r = process 'edit', 'web' => 'wiki1', 'id' => 'HomePage' assert_response(:success) assert_equal @wiki.read_page('wiki1', 'HomePage'), r.template_objects['page'] end def test_edit_page_locked_page @home.lock(Time.now, 'Locky') process 'edit', 'web' => 'wiki1', 'id' => 'HomePage' assert_redirected_to :web => 'wiki1', :controller => 'wiki', :action => 'locked', :id => 'HomePage' end def test_edit_page_break_lock @home.lock(Time.now, 'Locky') process 'edit', 'web' => 'wiki1', 'id' => 'HomePage', 'break_lock' => 'y' assert_response(:success) @home = Page.find(@home.id) assert @home.locked?(Time.now) end def test_edit_unknown_page process 'edit', 'web' => 'wiki1', 'id' => 'UnknownPage', 'break_lock' => 'y' assert_redirected_to :controller => 'wiki', :action => 'show', :web => 'wiki1', :id => 'HomePage' end def test_edit_page_with_special_symbols @wiki.write_page('wiki1', 'With : Special /> symbols', 'This page has special symbols in the name', Time.now, Author.new('Special', '127.0.0.3'), x_test_renderer) r = process 'edit', 'web' => 'wiki1', 'id' => 'With : Special /> symbols' assert_response(:success) xml = REXML::Document.new(r.body) form = REXML::XPath.first(xml, '//form') assert_equal '/wiki1/save/With+%3A+Special+%2F%3E+symbols', form.attributes['action'] end def test_export_xhtml @request.accept = 'application/xhtml+xml' # rollback homepage to a version that is easier to match @home.rollback(0, Time.now, 'Rick', x_test_renderer) r = process 'export_html', 'web' => 'wiki1' assert_response(:success, bypass_body_parsing = true) assert_equal 'application/zip', r.headers['Content-Type'] assert_match /attachment; filename="wiki1-xhtml-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, r.headers['Content-Disposition'] assert_equal 'PK', r.body[0..1], 'Content is not a zip file' # Tempfile doesn't know how to open files with binary flag, hence the two-step process Tempfile.open('instiki_export_file') { |f| @tempfile_path = f.path } begin File.open(@tempfile_path, 'wb') { |f| f.write(r.body); @exported_file = f.path } Zip::ZipFile.open(@exported_file) do |zip| assert_equal %w(Elephant.xhtml FirstPage.xhtml HomePage.xhtml MyWay.xhtml NoWikiWord.xhtml Oak.xhtml SmartEngine.xhtml ThatWay.xhtml index.xhtml liquor.xhtml), zip.dir.entries('.').sort assert_match /.*/, zip.file.read('Elephant.xhtml').gsub(/\s+/, ' ') assert_match /.*/, zip.file.read('Oak.xhtml').gsub(/\s+/, ' ') assert_match /.*/, zip.file.read('HomePage.xhtml').gsub(/\s+/, ' ') assert_equal '
', zip.file.read('index.xhtml').gsub(/\s+/, ' ') end ensure File.delete(@tempfile_path) if File.exist?(@tempfile_path) end end def test_export_html @request.accept = 'tex/html' # rollback homepage to a version that is easier to match @home.rollback(0, Time.now, 'Rick', x_test_renderer) r = process 'export_html', 'web' => 'wiki1' assert_response(:success, bypass_body_parsing = true) assert_equal 'application/zip', r.headers['Content-Type'] assert_match /attachment; filename="wiki1-html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, r.headers['Content-Disposition'] assert_equal 'PK', r.body[0..1], 'Content is not a zip file' # Tempfile doesn't know how to open files with binary flag, hence the two-step process Tempfile.open('instiki_export_file') { |f| @tempfile_path = f.path } begin File.open(@tempfile_path, 'wb') { |f| f.write(r.body); @exported_file = f.path } Zip::ZipFile.open(@exported_file) do |zip| assert_equal %w(Elephant.html FirstPage.html HomePage.html MyWay.html NoWikiWord.html Oak.html SmartEngine.html ThatWay.html index.html liquor.html), zip.dir.entries('.').sort assert_match /.*/, zip.file.read('Elephant.html').gsub(/\s+/, ' ') assert_match /.*/, zip.file.read('Oak.html').gsub(/\s+/, ' ') assert_match /.*/, zip.file.read('HomePage.html').gsub(/\s+/, ' ') assert_equal ' ', zip.file.read('index.html').gsub(/\s+/, ' ') end ensure File.delete(@tempfile_path) if File.exist?(@tempfile_path) end end def test_export_html_no_layout r = process 'export_html', 'web' => 'wiki1', 'layout' => 'no' assert_response(:success, bypass_body_parsing = true) assert_equal 'application/zip', r.headers['Content-Type'] assert_match /attachment; filename="wiki1-x?html-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, r.headers['Content-Disposition'] assert_equal 'PK', r.body[0..1], 'Content is not a zip file' end def test_export_markup r = process 'export_markup', 'web' => 'wiki1' assert_response(:success, bypass_body_parsing = true) assert_equal 'application/zip', r.headers['Content-Type'] assert_match /attachment; filename="wiki1-markdownMML-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.zip"/, r.headers['Content-Disposition'] assert_equal 'PK', r.body[0..1], 'Content is not a zip file' end if ENV['INSTIKI_TEST_LATEX'] or defined? $INSTIKI_TEST_PDFLATEX # def test_export_pdf # r = process 'export_pdf', 'web' => 'wiki1' # assert_response(:success, bypass_body_parsing = true) # assert_equal 'application/pdf', r.headers['Content-Type'] # assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/, # r.headers['Content-Disposition'] # assert_equal '%PDF', r.body[0..3] # assert_equal "EOF\n", r.body[-4..-1] # end else # puts 'Warning: tests involving pdflatex are very slow, therefore they are disabled by default.' # puts ' Set environment variable INSTIKI_TEST_PDFLATEX or global Ruby variable' # puts ' $INSTIKI_TEST_PDFLATEX to enable them.' end # def test_export_tex # r = process 'export_tex', 'web' => 'wiki1' # # assert_response(:success, bypass_body_parsing = true) # assert_equal 'application/octet-stream', r.headers['Content-Type'] # assert_match /attachment; filename="wiki1-tex-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.tex"/, # r.headers['Content-Disposition'] # assert_equal '\documentclass', r.body[0..13], 'Content is not a TeX file' # end def test_feeds process('feeds', 'web' => 'wiki1') end def test_index # delete extra web fixture webs(:instiki).destroy process('index') assert_redirected_to :web => 'wiki1', :controller => 'wiki', :action => 'show', :id => 'HomePage' end def test_index_multiple_webs @wiki.create_web('Test Wiki 2', 'wiki2') process('index') assert_redirected_to :controller => 'wiki', :action => 'web_list' end def test_index_multiple_webs_web_explicit @wiki.create_web('Test Wiki 2', 'wiki2') process('index', 'web' => 'wiki2') assert_redirected_to :web => 'wiki2', :controller => 'wiki', :action => 'show', :id => 'HomePage' end def test_index_wiki_not_initialized use_blank_wiki process('index') assert_redirected_to :controller => 'admin', :action => 'create_system' end def test_list r = process('list', 'web' => 'wiki1') assert_equal ['animals', 'trees'], r.template_objects['categories'] assert_nil r.template_objects['category'] assert_equal [@elephant, pages(:first_page), @home, pages(:my_way), pages(:no_wiki_word), @oak, pages(:smart_engine), pages(:that_way), @liquor], r.template_objects['pages_in_category'] end def test_locked @home.lock(Time.now, 'Locky') r = process('locked', 'web' => 'wiki1', 'id' => 'HomePage') assert_response(:success) assert_equal @home, r.template_objects['page'] end def test_login r = process 'login', 'web' => 'wiki1' assert_response(:success) # this action goes straight to the templates end def test_new r = process('new', 'id' => 'NewPage', 'web' => 'wiki1') assert_response(:success) assert_equal 'AnonymousCoward', r.template_objects['author'] assert_equal 'NewPage', r.template_objects['page_name'] end if ENV['INSTIKI_TEST_LATEX'] or defined? $INSTIKI_TEST_PDFLATEX # def test_pdf # assert RedClothForTex.available?, 'Cannot do test_pdf when pdflatex is not available' # r = process('pdf', 'web' => 'wiki1', 'id' => 'HomePage') # assert_response(:success, bypass_body_parsing = true) # # assert_equal '%PDF', r.body[0..3] # assert_equal "EOF\n", r.body[-4..-1] # # assert_equal 'application/pdf', r.headers['Content-Type'] # assert_match /attachment; filename="HomePage-wiki1-\d\d\d\d-\d\d-\d\d-\d\d-\d\d-\d\d.pdf"/, # r.headers['Content-Disposition'] # end end def test_print r = process('print', 'web' => 'wiki1', 'id' => 'HomePage') assert_response(:success) assert_equal :show, r.template_objects['link_mode'] end def test_source r = process('source', 'web' => 'wiki1', 'id' => 'HomePage') assert_response(:success) assert_match Regexp.new(Regexp.escape(%{HisWay would be MyWay $\\sin(x)\\begin{svg}&} + %{lt;svg/>\\end{svg}\\includegraphics\[width=3em\]{foo}$ in kinda} + %{ ThatWay in HisWay though MyWay \\OverThere -- see SmartEngine in t} + %{hat SmartEngineGUI})), r.body end def test_published set_web_property :published, true r = process('published', 'web' => 'wiki1', 'id' => 'HomePage') assert_response(:success) assert_equal @home, r.template_objects['page'] assert_match /That Way<\/a>/, r.body r = process('show', 'web' => 'wiki1', 'id' => 'HomePage') assert_response(:success) assert_equal @home, r.template_objects['page'] assert_match /That Way<\/a>/, r.body r = process 'save', 'web' => 'instiki', 'id' => 'HomePage', 'content' => 'Contents of a new page', 'author' => 'AuthorOfNewPage' assert_equal Web.find_by_address('instiki').has_page?('HomePage'), true r = process('published', 'web' => 'wiki1', 'id' => 'liquor') assert_response(:success) assert_equal @liquor, r.template_objects['page'] assert_match /go there<\/a>/, r.body r = process('show', 'web' => 'wiki1', 'id' => 'liquor') assert_response(:success) assert_equal @liquor, r.template_objects['page'] assert_match /go there<\/a>/, r.body Web.find_by_address('instiki').update_attribute(:published, true) r = process('published', 'web' => 'wiki1', 'id' => 'liquor') assert_response(:success) assert_equal @liquor, r.template_objects['page'] assert_match /go there<\/a>/, r.body r = process('show', 'web' => 'wiki1', 'id' => 'liquor') assert_response(:success) assert_equal @liquor, r.template_objects['page'] assert_match /go there<\/a>/, r.body set_web_property :published, false r = process('show', 'web' => 'wiki1', 'id' => 'liquor') assert_response(:success) assert_equal @liquor, r.template_objects['page'] assert_match /go there<\/a>/, r.body Web.find_by_address('instiki').update_attribute(:published, false) r = process('show', 'web' => 'wiki1', 'id' => 'liquor') assert_response(:success) assert_equal @liquor, r.template_objects['page'] assert_match /go there<\/a>/, r.body end def test_published_web_not_published set_web_property :published, false r = process('published', 'web' => 'wiki1', 'id' => 'HomePage') assert_response :missing end def test_published_should_render_homepage_if_no_page_specified set_web_property :published, true r = process('published', 'web' => 'wiki1') assert_response(:success) assert_equal @home, r.template_objects['page'] end def test_recently_revised r = process('recently_revised', 'web' => 'wiki1') assert_response(:success) assert_equal %w(animals trees), r.template_objects['categories'] assert_nil r.template_objects['category'] all_pages = @elephant, pages(:first_page), @home, pages(:my_way), pages(:no_wiki_word), @oak, pages(:smart_engine), pages(:that_way), @liquor assert_equal all_pages, r.template_objects['pages_in_category'] pages_by_day = r.template_objects['pages_by_day'] assert_not_nil pages_by_day pages_by_day_size = pages_by_day.keys.inject(0) { |sum, day| sum + pages_by_day[day].size } assert_equal all_pages.size, pages_by_day_size all_pages.each do |page| day = Date.new(page.revised_at.year, page.revised_at.month, page.revised_at.day) assert pages_by_day[day].include?(page) end assert_equal 'the web', r.template_objects['set_name'] end def test_recently_revised_with_categorized_page page2 = @wiki.write_page('wiki1', 'Page2', "Page2 contents.\n" + "category: categorized", Time.now, Author.new('AnotherAuthor', '127.0.0.2'), x_test_renderer) r = process('recently_revised', 'web' => 'wiki1') assert_response(:success) assert_equal %w(animals categorized trees), r.template_objects['categories'] # no category is specified in params assert_nil r.template_objects['category'] assert_equal [@elephant, pages(:first_page), @home, pages(:my_way), pages(:no_wiki_word), @oak, page2, pages(:smart_engine), pages(:that_way), @liquor], r.template_objects['pages_in_category'], "Pages are not as expected: " + r.template_objects['pages_in_category'].map {|p| p.name}.inspect assert_equal 'the web', r.template_objects['set_name'] end def test_recently_revised_with_categorized_page_multiple_categories r = process('recently_revised', 'web' => 'wiki1') assert_response(:success) assert_equal ['animals', 'trees'], r.template_objects['categories'] # no category is specified in params assert_nil r.template_objects['category'] assert_equal [@elephant, pages(:first_page), @home, pages(:my_way), pages(:no_wiki_word), @oak, pages(:smart_engine), pages(:that_way), @liquor], r.template_objects['pages_in_category'], "Pages are not as expected: " + r.template_objects['pages_in_category'].map {|p| p.name}.inspect assert_equal 'the web', r.template_objects['set_name'] end def test_recently_revised_with_specified_category r = process('recently_revised', 'web' => 'wiki1', 'category' => 'animals') assert_response(:success) assert_equal ['animals', 'trees'], r.template_objects['categories'] # no category is specified in params assert_equal 'animals', r.template_objects['category'] assert_equal [@elephant], r.template_objects['pages_in_category'] assert_equal "category 'animals'", r.template_objects['set_name'] end def test_revision r = process 'revision', 'web' => 'wiki1', 'id' => 'HomePage', 'rev' => '1' assert_response(:success) assert_equal @home, r.template_objects['page'] assert_equal @home.revisions[0], r.template_objects['revision'] end def test_rollback # rollback shows a form where a revision can be edited. # its assigns the same as or revision home_page = Page.find(@home.id) assert !home_page.locked?(Time.now) r = process 'rollback', 'web' => 'wiki1', 'id' => 'HomePage', 'rev' => '1' assert_response(:success) assert_equal @home, r.template_objects['page'] assert_equal @home.revisions[0], r.template_objects['revision'] home_page = Page.find(@home.id) assert home_page.locked?(Time.now) end def test_atom_with_content r = process 'atom_with_content', 'web' => 'wiki1' assert_response(:success) pages = r.template_objects['pages_by_revision'] assert_equal [@elephant, @liquor, @oak, pages(:no_wiki_word), pages(:that_way), pages(:smart_engine), pages(:my_way), pages(:first_page), @home], pages, "Pages are not as expected: #{pages.map {|p| p.name}.inspect}" assert !r.template_objects['hide_description'] end def test_atom_with_content_when_blocked @web.update_attributes(:password => 'aaa', :published => false) @web = Web.find(@web.id) r = process 'atom_with_content', 'web' => 'wiki1' assert_equal 403, r.response_code end def test_atom_with_headlines @title_with_spaces = @wiki.write_page('wiki1', 'Title With Spaces', 'About spaces', 1.hour.ago, Author.new('TreeHugger', '127.0.0.2'), x_test_renderer) @request.host = 'localhost' @request.port = 8080 r = process 'atom_with_headlines', 'web' => 'wiki1' assert_response(:success) pages = r.template_objects['pages_by_revision'] assert_equal [@elephant, @liquor, @title_with_spaces, @oak, pages(:no_wiki_word), pages(:that_way), pages(:smart_engine), pages(:my_way), pages(:first_page), @home], pages, "Pages are not as expected: #{pages.map {|p| p.name}.inspect}" assert r.template_objects['hide_description'] xml = REXML::Document.new(r.body) expected_page_links = ['http://localhost:8080/wiki1/show/Elephant', 'http://localhost:8080/wiki1/show/Title+With+Spaces', 'http://localhost:8080/wiki1/show/Oak', 'http://localhost:8080/wiki1/show/NoWikiWord', 'http://localhost:8080/wiki1/show/ThatWay', 'http://localhost:8080/wiki1/show/SmartEngine', 'http://localhost:8080/wiki1/show/MyWay', 'http://localhost:8080/wiki1/show/FirstPage', 'http://localhost:8080/wiki1/show/HomePage', ] assert_tag :tag => 'link', :parent => {:tag => 'feed'}, :attributes => { :rel => 'alternate', :href => 'http://localhost:8080/wiki1/show/HomePage'} expected_page_links.each do |link| assert_tag :tag => 'link', :parent => {:tag => 'entry'}, :attributes => {:href => link } end end def test_atom_switch_links_to_published @web.update_attributes(:password => 'aaa', :published => true) @web = Web.find(@web.id) @request.host = 'foo.bar.info' @request.port = 80 r = process 'atom_with_headlines', 'web' => 'wiki1' assert_response(:success) xml = REXML::Document.new(r.body) expected_page_links = ['http://foo.bar.info/wiki1/published/Elephant', 'http://foo.bar.info/wiki1/published/Oak', 'http://foo.bar.info/wiki1/published/NoWikiWord', 'http://foo.bar.info/wiki1/published/ThatWay', 'http://foo.bar.info/wiki1/published/SmartEngine', 'http://foo.bar.info/wiki1/published/MyWay', 'http://foo.bar.info/wiki1/published/FirstPage', 'http://foo.bar.info/wiki1/published/HomePage'] assert_tag :tag => 'link', :parent =>{:tag =>'feed'}, :attributes => {:rel => 'alternate', :href => 'http://foo.bar.info/wiki1/published/HomePage'} expected_page_links.each do |link| assert_tag :tag => 'link', :parent => {:tag => 'entry'}, :attributes => {:href => link} end end # def test_atom_with_params # setup_wiki_with_30_pages # # r = process 'atom_with_headlines', 'web' => 'wiki1' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 15, pages.size, 15 # # r = process 'atom_with_headlines', 'web' => 'wiki1', 'limit' => '5' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 5, pages.size # # r = process 'atom_with_headlines', 'web' => 'wiki1', 'limit' => '25' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 25, pages.size # # r = process 'atom_with_headlines', 'web' => 'wiki1', 'limit' => 'all' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 38, pages.size # # r = process 'atom_with_headlines', 'web' => 'wiki1', 'start' => '1976-10-16' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 23, pages.size # # r = process 'atom_with_headlines', 'web' => 'wiki1', 'end' => '1976-10-16' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 15, pages.size # # r = process 'atom_with_headlines', 'web' => 'wiki1', 'start' => '1976-10-01', 'end' => '1976-10-06' # assert_response(:success) # pages = r.template_objects['pages_by_revision'] # assert_equal 5, pages.size # end def test_atom_title_with_ampersand # was ticket:143 # Since we're declaringRecursive-include:<\/p>\n\n
extra fun Recursive include detected: Foo \342\206\222 Foo<\/em><\/p>/, r.body.as_bytes
end
def test_recursive_include_III
@wiki.write_page('wiki1', 'Bar', "extra fun\n\n[[!include HomePage]]", Time.now,
Author.new('AnotherAuthor', '127.0.0.2'), x_test_renderer)
@wiki.write_page('wiki1', 'Foo', "[[!include Bar]]\n\n[[!include Bar]]", Time.now,
Author.new('AnotherAuthor', '127.0.0.2'), x_test_renderer)
@wiki.write_page('wiki1', 'HomePage', "Recursive-include:\n\n[[!include Foo]]", Time.now,
Author.new('AnotherAuthor', '127.0.0.2'), x_test_renderer)
r = process('show', 'id' => 'HomePage', 'web' => 'wiki1')
assert_response :success
assert_match / Recursive-include:<\/p>\n\n