comparison test/unit/helpers/application_helper_test.rb @ 909:cbb26bc654de redmine-1.3

Update to Redmine 1.3-stable branch (Redmine SVN rev 8964)
author Chris Cannam
date Fri, 24 Feb 2012 19:09:32 +0000
parents cbce1fd3b1b7
children 5e80956cc792 5f33065ddc4b
comparison
equal deleted inserted replaced
908:c6c2cbd0afee 909:cbb26bc654de
16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 require File.expand_path('../../../test_helper', __FILE__) 18 require File.expand_path('../../../test_helper', __FILE__)
19 19
20 class ApplicationHelperTest < ActionView::TestCase 20 class ApplicationHelperTest < ActionView::TestCase
21
22 fixtures :projects, :roles, :enabled_modules, :users, 21 fixtures :projects, :roles, :enabled_modules, :users,
23 :repositories, :changesets, 22 :repositories, :changesets,
24 :trackers, :issue_statuses, :issues, :versions, :documents, 23 :trackers, :issue_statuses, :issues, :versions, :documents,
25 :wikis, :wiki_pages, :wiki_contents, 24 :wikis, :wiki_pages, :wiki_contents,
26 :boards, :messages, 25 :boards, :messages, :news,
27 :attachments, 26 :attachments, :enumerations
28 :enumerations
29 27
30 def setup 28 def setup
31 super 29 super
30 set_tmp_attachments_directory
32 end 31 end
33 32
34 context "#link_to_if_authorized" do 33 context "#link_to_if_authorized" do
35 context "authorized user" do 34 context "authorized user" do
36 should "be tested" 35 should "be tested"
127 } 126 }
128 attachments = Attachment.find(:all) 127 attachments = Attachment.find(:all)
129 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) } 128 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
130 end 129 end
131 130
131 def test_attached_images_filename_extension
132 set_tmp_attachments_directory
133 a1 = Attachment.new(
134 :container => Issue.find(1),
135 :file => mock_file_with_options({:original_filename => "testtest.JPG"}),
136 :author => User.find(1))
137 assert a1.save
138 assert_equal "testtest.JPG", a1.filename
139 assert_equal "image/jpeg", a1.content_type
140 assert a1.image?
141
142 a2 = Attachment.new(
143 :container => Issue.find(1),
144 :file => mock_file_with_options({:original_filename => "testtest.jpeg"}),
145 :author => User.find(1))
146 assert a2.save
147 assert_equal "testtest.jpeg", a2.filename
148 assert_equal "image/jpeg", a2.content_type
149 assert a2.image?
150
151 a3 = Attachment.new(
152 :container => Issue.find(1),
153 :file => mock_file_with_options({:original_filename => "testtest.JPE"}),
154 :author => User.find(1))
155 assert a3.save
156 assert_equal "testtest.JPE", a3.filename
157 assert_equal "image/jpeg", a3.content_type
158 assert a3.image?
159
160 a4 = Attachment.new(
161 :container => Issue.find(1),
162 :file => mock_file_with_options({:original_filename => "Testtest.BMP"}),
163 :author => User.find(1))
164 assert a4.save
165 assert_equal "Testtest.BMP", a4.filename
166 assert_equal "image/x-ms-bmp", a4.content_type
167 assert a4.image?
168
169 to_test = {
170 'Inline image: !testtest.jpg!' =>
171 'Inline image: <img src="/attachments/download/' + a1.id.to_s + '" alt="" />',
172 'Inline image: !testtest.jpeg!' =>
173 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '" alt="" />',
174 'Inline image: !testtest.jpe!' =>
175 'Inline image: <img src="/attachments/download/' + a3.id.to_s + '" alt="" />',
176 'Inline image: !testtest.bmp!' =>
177 'Inline image: <img src="/attachments/download/' + a4.id.to_s + '" alt="" />',
178 }
179
180 attachments = [a1, a2, a3, a4]
181 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
182 end
183
184 def test_attached_images_should_read_later
185 Attachment.storage_path = "#{Rails.root}/test/fixtures/files"
186 a1 = Attachment.find(16)
187 assert_equal "testfile.png", a1.filename
188 assert a1.readable?
189 assert (! a1.visible?(User.anonymous))
190 assert a1.visible?(User.find(2))
191 a2 = Attachment.find(17)
192 assert_equal "testfile.PNG", a2.filename
193 assert a2.readable?
194 assert (! a2.visible?(User.anonymous))
195 assert a2.visible?(User.find(2))
196 assert a1.created_on < a2.created_on
197
198 to_test = {
199 'Inline image: !testfile.png!' =>
200 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '" alt="" />',
201 'Inline image: !Testfile.PNG!' =>
202 'Inline image: <img src="/attachments/download/' + a2.id.to_s + '" alt="" />',
203 }
204 attachments = [a1, a2]
205 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :attachments => attachments) }
206 set_tmp_attachments_directory
207 end
208
132 def test_textile_external_links 209 def test_textile_external_links
133 to_test = { 210 to_test = {
134 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>', 211 'This is a "link":http://foo.bar' => 'This is a <a href="http://foo.bar" class="external">link</a>',
135 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>', 212 'This is an intern "link":/foo/bar' => 'This is an intern <a href="/foo/bar">link</a>',
136 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>', 213 '"link (Link title)":http://foo.bar' => '<a href="http://foo.bar" title="Link title" class="external">link</a>',
161 :class => 'document') 238 :class => 'document')
162 239
163 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2}, 240 version_link = link_to('1.0', {:controller => 'versions', :action => 'show', :id => 2},
164 :class => 'version') 241 :class => 'version')
165 242
243 board_url = {:controller => 'boards', :action => 'show', :id => 2, :project_id => 'ecookbook'}
244
166 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4} 245 message_url = {:controller => 'messages', :action => 'show', :board_id => 1, :id => 4}
246
247 news_url = {:controller => 'news', :action => 'show', :id => 1}
167 248
168 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'} 249 project_url = {:controller => 'projects', :action => 'show', :id => 'subproject1'}
169 250
170 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']} 251 source_url = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file']}
171 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']} 252 source_url_with_ext = {:controller => 'repositories', :action => 'entry', :id => 'ecookbook', :path => ['some', 'file.ext']}
196 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'), 277 'source:/some/file.ext@52' => link_to('source:/some/file.ext@52', source_url_with_ext.merge(:rev => 52), :class => 'source'),
197 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'), 278 'source:/some/file#L110' => link_to('source:/some/file#L110', source_url.merge(:anchor => 'L110'), :class => 'source'),
198 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'), 279 'source:/some/file.ext#L110' => link_to('source:/some/file.ext#L110', source_url_with_ext.merge(:anchor => 'L110'), :class => 'source'),
199 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'), 280 'source:/some/file@52#L110' => link_to('source:/some/file@52#L110', source_url.merge(:rev => 52, :anchor => 'L110'), :class => 'source'),
200 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'), 281 'export:/some/file' => link_to('export:/some/file', source_url.merge(:format => 'raw'), :class => 'source download'),
282 # forum
283 'forum#2' => link_to('Discussion', board_url, :class => 'board'),
284 'forum:Discussion' => link_to('Discussion', board_url, :class => 'board'),
201 # message 285 # message
202 'message#4' => link_to('Post 2', message_url, :class => 'message'), 286 'message#4' => link_to('Post 2', message_url, :class => 'message'),
203 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'), 287 'message#5' => link_to('RE: post 2', message_url.merge(:anchor => 'message-5', :r => 5), :class => 'message'),
288 # news
289 'news#1' => link_to('eCookbook first release !', news_url, :class => 'news'),
290 'news:"eCookbook first release !"' => link_to('eCookbook first release !', news_url, :class => 'news'),
204 # project 291 # project
205 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'), 292 'project#3' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
206 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'), 293 'project:subproject1' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
207 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'), 294 'project:"eCookbook subProject 1"' => link_to('eCookbook Subproject 1', project_url, :class => 'project'),
208 # escaping 295 # escaping
237 'document:"Test document"' => 'document:"Test document"', 324 'document:"Test document"' => 'document:"Test document"',
238 'ecookbook:document:"Test document"' => '<a href="/documents/1" class="document">Test document</a>', 325 'ecookbook:document:"Test document"' => '<a href="/documents/1" class="document">Test document</a>',
239 'invalid:document:"Test document"' => 'invalid:document:"Test document"', 326 'invalid:document:"Test document"' => 'invalid:document:"Test document"',
240 # versions 327 # versions
241 'version:"1.0"' => 'version:"1.0"', 328 'version:"1.0"' => 'version:"1.0"',
242 'ecookbook:version:"1.0"' => '<a href="/versions/show/2" class="version">1.0</a>', 329 'ecookbook:version:"1.0"' => '<a href="/versions/2" class="version">1.0</a>',
243 'invalid:version:"1.0"' => 'invalid:version:"1.0"', 330 'invalid:version:"1.0"' => 'invalid:version:"1.0"',
244 # changeset 331 # changeset
245 'r2' => 'r2', 332 'r2' => 'r2',
246 'ecookbook:r2' => changeset_link, 333 'ecookbook:r2' => changeset_link,
247 'invalid:r2' => 'invalid:r2', 334 'invalid:r2' => 'invalid:r2',
348 435
349 def test_wiki_links 436 def test_wiki_links
350 to_test = { 437 to_test = {
351 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>', 438 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
352 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>', 439 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
440 # title content should be formatted
441 '[[Another page|With _styled_ *title*]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With <em>styled</em> <strong>title</strong></a>',
442 '[[Another page|With title containing <strong>HTML entities &amp; markups</strong>]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">With title containing &lt;strong&gt;HTML entities &amp; markups&lt;/strong&gt;</a>',
353 # link with anchor 443 # link with anchor
354 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>', 444 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
355 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>', 445 '[[Another page#anchor|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page#anchor" class="wiki-page">Page</a>',
356 # page that doesn't exist 446 # page that doesn't exist
357 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>', 447 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
369 '![[Another page|Page]]' => '[[Another page|Page]]', 459 '![[Another page|Page]]' => '[[Another page|Page]]',
370 # project does not exist 460 # project does not exist
371 '[[unknowproject:Start]]' => '[[unknowproject:Start]]', 461 '[[unknowproject:Start]]' => '[[unknowproject:Start]]',
372 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]', 462 '[[unknowproject:Start|Page title]]' => '[[unknowproject:Start|Page title]]',
373 } 463 }
464
374 @project = Project.find(1) 465 @project = Project.find(1)
375 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) } 466 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text) }
467 end
468
469 def test_wiki_links_within_local_file_generation_context
470
471 to_test = {
472 # link to a page
473 '[[CookBook documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">CookBook documentation</a>',
474 '[[CookBook documentation|documentation]]' => '<a href="CookBook_documentation.html" class="wiki-page">documentation</a>',
475 '[[CookBook documentation#One-section]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">CookBook documentation</a>',
476 '[[CookBook documentation#One-section|documentation]]' => '<a href="CookBook_documentation.html#One-section" class="wiki-page">documentation</a>',
477 # page that doesn't exist
478 '[[Unknown page]]' => '<a href="Unknown_page.html" class="wiki-page new">Unknown page</a>',
479 '[[Unknown page|404]]' => '<a href="Unknown_page.html" class="wiki-page new">404</a>',
480 '[[Unknown page#anchor]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">Unknown page</a>',
481 '[[Unknown page#anchor|404]]' => '<a href="Unknown_page.html#anchor" class="wiki-page new">404</a>',
482 }
483
484 @project = Project.find(1)
485
486 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :local) }
376 end 487 end
377 488
378 def test_html_tags 489 def test_html_tags
379 to_test = { 490 to_test = {
380 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>", 491 "<div>content</div>" => "<p>&lt;div&gt;content&lt;/div&gt;</p>",
477 # Some ruby code here 588 # Some ruby code here
478 </code></pre> 589 </code></pre>
479 RAW 590 RAW
480 591
481 expected = <<-EXPECTED 592 expected = <<-EXPECTED
482 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="no">1</span> <span class="c"># Some ruby code here</span></span> 593 <pre><code class="ruby syntaxhl"><span class=\"CodeRay\"><span class="line-numbers">1</span><span class="comment"># Some ruby code here</span></span>
483 </code></pre> 594 </code></pre>
484 EXPECTED 595 EXPECTED
485 596
486 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '') 597 assert_equal expected.gsub(%r{[\r\n\t]}, ''), textilizable(raw).gsub(%r{[\r\n\t]}, '')
487 end 598 end
529 def test_headings 640 def test_headings
530 raw = 'h1. Some heading' 641 raw = 'h1. Some heading'
531 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>| 642 expected = %|<a name="Some-heading"></a>\n<h1 >Some heading<a href="#Some-heading" class="wiki-anchor">&para;</a></h1>|
532 643
533 assert_equal expected, textilizable(raw) 644 assert_equal expected, textilizable(raw)
645 end
646
647 def test_headings_with_special_chars
648 # This test makes sure that the generated anchor names match the expected
649 # ones even if the heading text contains unconventional characters
650 raw = 'h1. Some heading related to version 0.5'
651 anchor = sanitize_anchor_name("Some-heading-related-to-version-0.5")
652 expected = %|<a name="#{anchor}"></a>\n<h1 >Some heading related to version 0.5<a href="##{anchor}" class="wiki-anchor">&para;</a></h1>|
653
654 assert_equal expected, textilizable(raw)
655 end
656
657 def test_wiki_links_within_wiki_page_context
658
659 page = WikiPage.find_by_title('Another_page' )
660
661 to_test = {
662 # link to another page
663 '[[CookBook documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">CookBook documentation</a>',
664 '[[CookBook documentation|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation" class="wiki-page">documentation</a>',
665 '[[CookBook documentation#One-section]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">CookBook documentation</a>',
666 '[[CookBook documentation#One-section|documentation]]' => '<a href="/projects/ecookbook/wiki/CookBook_documentation#One-section" class="wiki-page">documentation</a>',
667 # link to the current page
668 '[[Another page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Another page</a>',
669 '[[Another page|Page]]' => '<a href="/projects/ecookbook/wiki/Another_page" class="wiki-page">Page</a>',
670 '[[Another page#anchor]]' => '<a href="#anchor" class="wiki-page">Another page</a>',
671 '[[Another page#anchor|Page]]' => '<a href="#anchor" class="wiki-page">Page</a>',
672 # page that doesn't exist
673 '[[Unknown page]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">Unknown page</a>',
674 '[[Unknown page|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page" class="wiki-page new">404</a>',
675 '[[Unknown page#anchor]]' => '<a href="/projects/ecookbook/wiki/Unknown_page#anchor" class="wiki-page new">Unknown page</a>',
676 '[[Unknown page#anchor|404]]' => '<a href="/projects/ecookbook/wiki/Unknown_page#anchor" class="wiki-page new">404</a>',
677 }
678
679 @project = Project.find(1)
680
681 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(WikiContent.generate!( :text => text, :page => page ), :text) }
682 end
683
684 def test_wiki_links_anchor_option_should_prepend_page_title_to_href
685
686 to_test = {
687 # link to a page
688 '[[CookBook documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">CookBook documentation</a>',
689 '[[CookBook documentation|documentation]]' => '<a href="#CookBook_documentation" class="wiki-page">documentation</a>',
690 '[[CookBook documentation#One-section]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">CookBook documentation</a>',
691 '[[CookBook documentation#One-section|documentation]]' => '<a href="#CookBook_documentation_One-section" class="wiki-page">documentation</a>',
692 # page that doesn't exist
693 '[[Unknown page]]' => '<a href="#Unknown_page" class="wiki-page new">Unknown page</a>',
694 '[[Unknown page|404]]' => '<a href="#Unknown_page" class="wiki-page new">404</a>',
695 '[[Unknown page#anchor]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">Unknown page</a>',
696 '[[Unknown page#anchor|404]]' => '<a href="#Unknown_page_anchor" class="wiki-page new">404</a>',
697 }
698
699 @project = Project.find(1)
700
701 to_test.each { |text, result| assert_equal "<p>#{result}</p>", textilizable(text, :wiki_links => :anchor) }
702 end
703
704 def test_headings_in_wiki_single_page_export_should_be_prepended_with_page_title
705 page = WikiPage.generate!( :title => 'Page Title' )
706 content = WikiContent.generate!( :text => 'h1. Some heading', :page => page )
707
708 expected = %|<a name="Page_Title_Some-heading"></a>\n<h1 >Some heading<a href="#Page_Title_Some-heading" class="wiki-anchor">&para;</a></h1>|
709
710 assert_equal expected, textilizable(content, :text, :wiki_links => :anchor )
534 end 711 end
535 712
536 def test_table_of_content 713 def test_table_of_content
537 raw = <<-RAW 714 raw = <<-RAW
538 {{toc}} 715 {{toc}}
586 '</ul>' + 763 '</ul>' +
587 '</li>' + 764 '</li>' +
588 '</ul>' 765 '</ul>'
589 766
590 @project = Project.find(1) 767 @project = Project.find(1)
591 assert textilizable(raw).gsub("\n", "").include?(expected), textilizable(raw) 768 assert textilizable(raw).gsub("\n", "").include?(expected)
592 end 769 end
593 770
594 def test_table_of_content_should_contain_included_page_headings 771 def test_table_of_content_should_contain_included_page_headings
595 raw = <<-RAW 772 raw = <<-RAW
596 {{toc}} 773 {{toc}}
673 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>), 850 assert_equal %(<a href="http://test.host/projects/ecookbook?jump=blah">eCookbook</a>),
674 link_to_project(project, {:only_path => false, :jump => 'blah'}) 851 link_to_project(project, {:only_path => false, :jump => 'blah'})
675 assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>), 852 assert_equal %(<a href="/projects/ecookbook/settings" class="project">eCookbook</a>),
676 link_to_project(project, {:action => 'settings'}, :class => "project") 853 link_to_project(project, {:action => 'settings'}, :class => "project")
677 end 854 end
855
856 def test_principals_options_for_select_with_users
857 users = [User.find(2), User.find(4)]
858 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>),
859 principals_options_for_select(users)
860 end
861
862 def test_principals_options_for_select_with_selected
863 users = [User.find(2), User.find(4)]
864 assert_equal %(<option value="2">John Smith</option><option value="4" selected="selected">Robert Hill</option>),
865 principals_options_for_select(users, User.find(4))
866 end
867
868 def test_principals_options_for_select_with_users_and_groups
869 users = [User.find(2), Group.find(11), User.find(4), Group.find(10)]
870 assert_equal %(<option value="2">John Smith</option><option value="4">Robert Hill</option>) +
871 %(<optgroup label="Groups"><option value="10">A Team</option><option value="11">B Team</option></optgroup>),
872 principals_options_for_select(users)
873 end
874
875 def test_principals_options_for_select_with_empty_collection
876 assert_equal '', principals_options_for_select([])
877 end
678 end 878 end