summaryrefslogtreecommitdiff
path: root/t/04-template-handling.t
blob: 7043f53f5e2374026ab3526dc4e7c32f6dc74b64 (plain)
  1. #!/usr/bin/perl
  2. use strict;
  3. use warnings;
  4. # Absolute directory name required to not trip up Template::Latex
  5. $ENV{TMPDIR} = "$ENV{PWD}/t/var";
  6. use Test::More 'no_plan';
  7. use Test::Trap qw(trap $trap);
  8. use Test::Exception;
  9. use Error qw(:try :warndie);
  10. use LedgerSMB::AM;
  11. use LedgerSMB::Form;
  12. use LedgerSMB::Sysconfig;
  13. use LedgerSMB::Locale;
  14. use LedgerSMB::Template;
  15. use LedgerSMB::Template::Elements;
  16. use LedgerSMB::Template::CSV;
  17. use LedgerSMB::Template::HTML;
  18. use LedgerSMB::Template::LaTeX;
  19. use LedgerSMB::Template::TXT;
  20. $LedgerSMB::Sysconfig::tempdir = 't/var';
  21. my @r;
  22. my $temp;
  23. my $form;
  24. my $myconfig;
  25. my $template;
  26. my $FH;
  27. my $locale;
  28. $locale = LedgerSMB::Locale->get_handle('fr');
  29. ##############
  30. ## AM tests ##
  31. ##############
  32. # AM->check_template_name checks
  33. # check_template operates by calling $form->error if the checks fail
  34. $form = new Form;
  35. $myconfig = {'templates' => 'test'};
  36. for my $ext ('css', 'tex', 'txt', 'html', 'xml') {
  37. $form->{file} = "test/apples.${ext}";
  38. @r = trap{AM->check_template_name($myconfig, $form)};
  39. ok(!defined $trap->die,
  40. "AM, check_template_name: Template directory, ${ext}");
  41. }
  42. $form->{file} = 'css/apples.txt';
  43. @r = trap{AM->check_template_name($myconfig, $form)};
  44. ok(!defined $trap->die,
  45. 'AM, check_template_name: CSS directory, txt');
  46. $form->{file} = 'test2/apples.txt';
  47. @r = trap{AM->check_template_name($myconfig, $form)};
  48. is($trap->die, "Error: Not in a whitelisted directory: test2/apples.txt\n",
  49. 'AM, check_template_name: Invalid directory, non-css denial');
  50. $form->{file} = 'test/apples.exe';
  51. @r = trap{AM->check_template_name($myconfig, $form)};
  52. is($trap->die, "Error: Error: File is of type that is not allowed.\n",
  53. 'AM, check_template_name: Disallowed type denial');
  54. # adjusting backuppath to avoid triggering directory traversal detection
  55. $temp = ${LedgerSMB::Sysconfig::backuppath};
  56. ${LedgerSMB::Sysconfig::backuppath} = "foo";
  57. $form->{file} = "${LedgerSMB::Sysconfig::backuppath}/apples.txt";
  58. @r = trap{AM->check_template_name($myconfig, $form)};
  59. is($trap->die, "Error: Not allowed to access foo/ with this method\n",
  60. 'AM, check_template_name: Backup path denial');
  61. ${LedgerSMB::Sysconfig::backuppath} = $temp;
  62. $form->{file} = "css/../apples.txt";
  63. @r = trap{AM->check_template_name($myconfig, $form)};
  64. is($trap->die, "Error: Directory transversal not allowed.\n",
  65. 'AM, check_template_name: Directory transversal denial 1');
  66. $form->{file} = "/tmp/apples.txt";
  67. @r = trap{AM->check_template_name($myconfig, $form)};
  68. is($trap->die, "Error: Directory transversal not allowed.\n",
  69. 'AM, check_template_name: Directory transversal denial 2');
  70. $form->{file} = "test/apples.txt:evil";
  71. @r = trap{AM->check_template_name($myconfig, $form)};
  72. is($trap->die, "Error: Directory transversal not allowed.\n",
  73. 'AM, check_template_name: Directory transversal denial 3');
  74. $form->{file} = "c:\\evil.txt";
  75. @r = trap{AM->check_template_name($myconfig, $form)};
  76. is($trap->die, "Error: Directory transversal not allowed.\n",
  77. 'AM, check_template_name: Directory transversal denial 4');
  78. # AM->load_template checks
  79. # load_template takes its file name from form
  80. $form = new Form;
  81. $myconfig = {'templates' => 't/data'};
  82. $form->{file} = 't/data/04-not-there.txt';
  83. @r = trap{AM->load_template($myconfig, $form)};
  84. is($trap->die, "Error: t/data/04-not-there.txt : No such file or directory\n",
  85. 'AM, load_template: Die on non-existent file');
  86. $form->{file} = 't/data/04-template.html';
  87. AM->load_template($myconfig, $form);
  88. is($form->{body}, "I am a template.\nLook at me <?lsmb login ?>.\n",
  89. 'AM, load_template: Read existing file');
  90. # AM->save_template checks
  91. $form = new Form;
  92. $myconfig = {'templates' => 't/var/not here'};
  93. $form->{body} = "I am a template.\nLook at me.\n";
  94. $form->{file} = "$myconfig->{templates}/test.txt";
  95. @r = trap{AM->save_template($myconfig, $form)};
  96. is($trap->die,
  97. "Error: t/var/not here/test.txt : No such file or directory\n",
  98. 'AM, save_template: Die on unwritable file');
  99. $myconfig = {'templates' => 't/var'};
  100. $form->{body} = "I am a template.\nLook at me.";
  101. $form->{file} = "$myconfig->{templates}/04-template-save-test-$$.txt";
  102. ok(!-e $form->{file}, 'AM, save_template: Environment clean');
  103. AM->save_template($myconfig, $form);
  104. ok(-e $form->{file}, 'AM, save_template: File created');
  105. open($FH, '<', $form->{file});
  106. @r = <$FH>;
  107. close($FH);
  108. chomp(@r);
  109. is(join("\n", @r), $form->{body}, 'AM, save_template: Good save');
  110. is(unlink($form->{file}), 1, 'AM, save_template: removing testfile');
  111. ok(!-e $form->{file}, 'AM, save_template: testfile removed');
  112. ######################################
  113. ## LedgerSMB::Template::HTML checks ##
  114. ######################################
  115. is(LedgerSMB::Template::HTML::get_template('04-template'), '04-template.html',
  116. 'HTML, get_template: Returned correct template file name');
  117. is(LedgerSMB::Template::HTML::preprocess('04-template'), '04-template',
  118. 'HTML, preprocess: Returned simple string unchanged');
  119. is(LedgerSMB::Template::HTML::preprocess('14 > 12'), '14 &gt; 12',
  120. 'HTML, preprocess: Returned properly escaped string');
  121. is_deeply(LedgerSMB::Template::HTML::preprocess([0, 'apple', 'mango&durian']),
  122. [0, 'apple', 'mango&amp;durian'],
  123. 'HTML, preprocess: Returned properly escaped array ref contents');
  124. is_deeply(LedgerSMB::Template::HTML::preprocess({'fruit' => '&veggies',
  125. 'test' => 1}),
  126. {'fruit' => '&amp;veggies', 'test' => 1},
  127. 'HTML, preprocess: Returned properly escaped hash ref contents');
  128. is_deeply(LedgerSMB::Template::HTML::preprocess({'fruit' => '&veggies',
  129. 'test' => ['nest', 'bird', '0 < 15', 1]}),
  130. {'fruit' => '&amp;veggies', 'test' => ['nest', 'bird', '0 &lt; 15', 1]},
  131. 'HTML, preprocess: Returned properly escaped nested contents');
  132. is(LedgerSMB::Template::HTML::postprocess({outputfile => '04-template'}),
  133. '04-template.html', 'HTML, postprocess: Return output filename');
  134. ####################
  135. ## Template tests ##
  136. ####################
  137. # Template->new
  138. $myconfig = {'templates' => 't/data'};
  139. throws_ok{new LedgerSMB::Template('user' => $myconfig, 'language' => 'x/0', 'format' => 'HTML')}
  140. qr/Invalid language/, 'Template, new: Invalid language caught 1';
  141. throws_ok{new LedgerSMB::Template('user' => $myconfig, 'language' => '1\\2', 'format' => 'HTML')}
  142. qr/Invalid language/, 'Template, new: Invalid language caught 2';
  143. throws_ok{new LedgerSMB::Template('user' => $myconfig, 'language' => '1:2', 'format' => 'HTML')}
  144. qr/Invalid language/, 'Template, new: Invalid language caught 3';
  145. throws_ok{new LedgerSMB::Template('user' => $myconfig, 'language' => '..', 'format' => 'HTML')}
  146. qr/Invalid language/, 'Template, new: Invalid language caught 4';
  147. throws_ok{new LedgerSMB::Template('user' => $myconfig, 'language' => '.svn', 'format' => 'HTML')}
  148. qr/Invalid language/,
  149. 'Template, new: Invalid language caught 5';
  150. $template = undef;
  151. $template = new LedgerSMB::Template('user' => $myconfig, 'language' => 'de', 'format' => 'HTML');
  152. ok(defined $template, 'Template, new: Object creation with valid language');
  153. isa_ok($template, 'LedgerSMB::Template',
  154. 'Template, new: Object creation with valid language');
  155. is($template->{include_path}, 't/data/de;t/data',
  156. 'Template, new: Object creation with valid language has good include_path');
  157. $template = undef;
  158. $template = new LedgerSMB::Template('user' => $myconfig, 'language' => 'de',
  159. 'path' => 't/data', 'output_file' => 'test', 'format' => 'HTML');
  160. ok(defined $template,
  161. 'Template, new: Object creation with valid language and path');
  162. isa_ok($template, 'LedgerSMB::Template',
  163. 'Template, new: Object creation with valid language and path');
  164. is($template->{include_path}, 't/data',
  165. 'Template, new: Object creation with valid path overrides language');
  166. is($template->{outputfile}, 't/var/test',
  167. 'Template, new: Object creation with filename is correct');
  168. $template = undef;
  169. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'HTML',
  170. 'template' => '04-template', 'locale' => $locale);
  171. ok(defined $template,
  172. 'Template, new: Object creation with locale');
  173. isa_ok($template, 'LedgerSMB::Template',
  174. 'Template, new: Object creation with locale');
  175. $template = undef;
  176. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'HTML',
  177. 'template' => '04-template-2', 'no_auto_output' => 1);
  178. ok(defined $template,
  179. 'Template, new: Object creation with non-existent template');
  180. throws_ok{$template->render({'login' => 'foo'})} qr/not found/,
  181. 'Template, render: File not found caught';
  182. $template = undef;
  183. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'TODO',
  184. 'template' => '04-template', 'no_auto_output' => 1);
  185. ok(defined $template,
  186. 'Template, new: Object creation with non-existent format');
  187. throws_ok{$template->render({'login' => 'foo'})} qr/Can't locate/,
  188. 'Template, render: Invalid format caught';
  189. #####################
  190. ## Rendering tests ##
  191. #####################
  192. $template = undef;
  193. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'PDF',
  194. 'template' => '04-template', 'no_auto_output' => 1);
  195. ok(defined $template,
  196. 'Template, new (PDF): Object creation with format and template');
  197. isa_ok($template, 'LedgerSMB::Template',
  198. 'Template, new (PDF): Object creation with format and template');
  199. is($template->{include_path}, 't/data',
  200. 'Template, new (PDF): Object creation with format and template');
  201. is($template->render({'login' => 'foo&bar'}), "t/var/04-template-output-$$.pdf",
  202. 'Template, render (PDF): Simple PDF template, default filename');
  203. ok(-e "t/var/04-template-output-$$.pdf",
  204. 'Template, render (PDF): File created');
  205. is(unlink("t/var/04-template-output-$$.pdf"), 1,
  206. 'Template, render (PDF): removing testfile');
  207. ok(!-e "t/var/04-template-output-$$.pdf",
  208. 'Template, render (PDF): testfile removed');
  209. $template = undef;
  210. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'PS',
  211. 'template' => '04-template', 'no_auto_output' => 1);
  212. ok(defined $template,
  213. 'Template, new (PS): Object creation with format and template');
  214. isa_ok($template, 'LedgerSMB::Template',
  215. 'Template, new (PS): Object creation with format and template');
  216. is($template->{include_path}, 't/data',
  217. 'Template, new (PS): Object creation with format and template');
  218. is($template->render({'login' => 'foo\&bar'}),
  219. "t/var/04-template-output-$$.ps",
  220. 'Template, render (PS): Simple Postscript template, default filename');
  221. ok(-e "t/var/04-template-output-$$.ps", 'Template, render (PS): File created');
  222. is(unlink("t/var/04-template-output-$$.ps"), 1,
  223. 'Template, render (PS): removing testfile');
  224. ok(!-e "t/var/04-template-output-$$.ps",
  225. 'Template, render (PS): testfile removed');
  226. $template = undef;
  227. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'TXT',
  228. 'template' => '04-template', 'no_auto_output' => 1);
  229. ok(defined $template,
  230. 'Template, new (TXT): Object creation with format and template');
  231. isa_ok($template, 'LedgerSMB::Template',
  232. 'Template, new (TXT): Object creation with format and template');
  233. is($template->{include_path}, 't/data',
  234. 'Template, new (TXT): Object creation with format and template');
  235. is($template->render({'login' => 'foo&bar'}),
  236. undef,
  237. 'Template, render: Simple text template, no filename');
  238. is($template->{output}, "I am a template.\nLook at me foo&bar.\n",
  239. 'Template, render (TXT): Simple TXT template, correct output');
  240. $template = undef;
  241. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'HTML',
  242. 'template' => '04-template', 'no_auto_output' => 1);
  243. ok(defined $template,
  244. 'Template, new (HTML): Object creation with format and template');
  245. isa_ok($template, 'LedgerSMB::Template',
  246. 'Template, new (HTML): Object creation with format and template');
  247. is($template->{include_path}, 't/data',
  248. 'Template, new (HTML): Object creation with format and template');
  249. is($template->render({'login' => 'foo&bar'}),
  250. undef,
  251. 'Template, render (HTML): Simple HTML template, no file');
  252. is($template->{output}, "I am a template.\nLook at me foo&amp;bar.",
  253. 'Template, render (HTML): Simple HTML template, correct output');
  254. $template = undef;
  255. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'HTML',
  256. 'template' => \'Look at me <?lsmb login ?>.', 'no_auto_output' => 1);
  257. ok(defined $template,
  258. 'Template, new (HTML): Object creation with string template');
  259. isa_ok($template, 'LedgerSMB::Template',
  260. 'Template, new (HTML): Object creation with string template');
  261. is($template->{include_path}, 't/data',
  262. 'Template, new (HTML): Object creation with string template');
  263. is($template->render({'login' => 'foo&bar'}),
  264. undef,
  265. 'Template, render (HTML): Simple HTML string template, no file');
  266. is($template->{output}, "Look at me foo&amp;bar.",
  267. 'Template, render (HTML): Simple HTML string template, correct output');
  268. $template = undef;
  269. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'HTML',
  270. 'template' => '04-gettext', 'output_file' => '04-gettext',
  271. 'no_auto_output' => 1);
  272. ok(defined $template,
  273. 'Template, new (HTML): Object creation with outputfile');
  274. isa_ok($template, 'LedgerSMB::Template',
  275. 'Template, new (HTML): Object creation with outputfile');
  276. is($template->{include_path}, 't/data',
  277. 'Template, new (HTML): Object creation with outputfile');
  278. is($template->render({'month' => 'June', 'login' => 'foo&bar',
  279. 'fr' => $locale}), 't/var/04-gettext.html',
  280. 'Template, render (HTML): Gettext HTML template');
  281. ok(-e "t/var/04-gettext.html",
  282. 'Template, render (HTML): File created');
  283. open($FH, '<', "t/var/04-gettext.html");
  284. @r = <$FH>;
  285. close($FH);
  286. chomp(@r);
  287. is(join("\n", @r),
  288. "I am a foo&amp;bar.\nLook at me Juin.\njuni\nAan foo&amp;bar",
  289. 'Template, render (HTML): Gettext HTML template, correct output');
  290. is(unlink("t/var/04-gettext.html"), 1,
  291. 'Template, render (HTML): removing testfile');
  292. ok(!-e "t/var/04-gettext.html",
  293. 'Template, render (HTML): testfile removed');
  294. ## XeTeX test, requires PDFLATEX to be xelatex and modified Template::Latex
  295. SKIP: {
  296. skip 'XeTeX and modified Template::Latex requiring PDF tests';
  297. $template = undef;
  298. $template = new LedgerSMB::Template('user' => $myconfig,
  299. 'format' => 'PDF', 'template' => '04-gettext',
  300. 'no_auto_output' => 1);
  301. ok(defined $template,
  302. 'Template, new (PDF): XeTeX template creation');
  303. isa_ok($template, 'LedgerSMB::Template',
  304. 'Template, new (PDF): XeTeX template creation');
  305. is($template->{include_path}, 't/data',
  306. 'Template, new (PDF): XeTeX template creation');
  307. is($template->render({'login' => 'foo&bar'}),
  308. "t/var/04-gettext-output-$$.pdf",
  309. 'Template, render (PDF): XeTeX PDF template, default filename');
  310. ok(-e "t/var/04-gettext-output-$$.pdf",
  311. 'Template, render (PDF): File created');
  312. is(unlink("t/var/04-gettext-output-$$.pdf"), 1,
  313. 'Template, render (PDF): removing testfile');
  314. ok(!-e "t/var/04-gettext-output-$$.pdf",
  315. 'Template, render (PDF): testfile removed');
  316. }
  317. #########################################
  318. ## LedgerSMB::Template private methods ##
  319. #########################################
  320. use Math::BigFloat;
  321. $template = undef;
  322. $template = new LedgerSMB::Template('user' => {numberformat => '1.000,00'},
  323. 'format' => 'HTML', 'template' => '04-template', 'no_auto_output' => 1);
  324. ok(defined $template,
  325. 'Template, private (_preprocess): Object creation with format and template');
  326. isa_ok($template, 'LedgerSMB::Template',
  327. 'Template, private (_preprocess): Object creation with format and template');
  328. my $number = Math::BigFloat->new(17.5);
  329. isa_ok($number, 'Math::BigFloat',
  330. 'Template, private (_preprocess): number');
  331. $template->_preprocess($number);
  332. ## Commenting out these tests since currently the functionality is known broken
  333. ## and unused
  334. #cmp_ok($number, 'eq', '17,50',
  335. # 'Template, private (_preprocess): Math::BigFloat conversion');
  336. #$number = [Math::BigFloat->new(1008.51), 'hello'];
  337. #$template->_preprocess($number);
  338. #
  339. #cmp_ok($number->[0], 'eq', '1.008,51',
  340. # 'Template, private (_preprocess): Math::BigFloat conversion (array)');
  341. #cmp_ok($number->[1], 'eq', 'hello',
  342. # 'Template, private (_preprocess): no conversion (array)');
  343. ###################################
  344. ## LedgerSMB::Template::Elements ##
  345. ###################################
  346. $template = undef;
  347. $form = undef;
  348. my $lsmb = LedgerSMB->new();
  349. $locale = LedgerSMB::Locale->get_handle( ${LedgerSMB::Sysconfig::language} )
  350. or $lsmb->error( __FILE__ . ':' . __LINE__ . ": Locale not loaded: $!\n" );
  351. $template = new LedgerSMB::Template('user' => {numberformat => '1.000,00'},
  352. 'format' => 'HTML', path => 't/data', locale => $locale, 'template' => '04-complex_template', 'no_auto_output' => 1);
  353. $template->render({});
  354. my $contact_request = {
  355. entity_id => 1,
  356. control_code => 'test1',
  357. meta_number => 'test1',
  358. credit_id => '1',
  359. entity_class => 1,
  360. credit_list => [{ entity_class => 1,
  361. meta_number => 'test1',
  362. }],
  363. contacts => [{contact => 'ctest1',
  364. description => 'dtest1',
  365. contact_class => '1'}],
  366. business_id => 1000,
  367. business_types => [{ id => 1, description => 'test1' },
  368. { id => 1000, description => 'test2' }],
  369. }; # Company with Credit Accounts and business types.
  370. my $contact_template = LedgerSMB::Template->new(
  371. path => 'UI/Contact',
  372. template => 'contact',
  373. format => 'HTML',
  374. no_auto_output => 1,
  375. output_file => 'contact_test1'
  376. );
  377. $contact_template->render($contact_request);
  378. my @output = get_output_line_array($contact_template);
  379. is(grep (/value="1" selected/, @output), 0, 'Select box Value 1 unselected');
  380. is(grep (/value="1000" selected/, @output), 1, 'Select box Value 1000 selected');
  381. is(grep (/<td class="description">dtest1/, @output), 1, 'Contact description shows');
  382. # LPR PRinting Tests
  383. use LedgerSMB::Sysconfig;
  384. %LedgerSMB::Sysconfig::printer = ('test' => 'cat > t/var/04-lpr-test');
  385. $template = new LedgerSMB::Template('user' => $myconfig, 'format' => 'PDF',
  386. 'template' => '04-template', 'locale' => $locale, no_auto_output => 1);
  387. $template->render({media => 'test'});
  388. $template->output(media => 'test');
  389. ok (open (LPR_TEST, '<', 't/var/04-lpr-test'), 'LedgerSMB::Template::_output_lpr output file opened successfully');
  390. my $line1 = <LPR_TEST>;
  391. like($line1, qr/^%PDF/, 'output file is pdf');
  392. # Functions
  393. sub get_output_line_array {
  394. my $FH;
  395. my ($template) = @_;
  396. open($FH, '<:bytes', $template->{rendered}) or
  397. throw Error::Simple 'Unable to open rendered file';
  398. my @lines = <$FH>;
  399. close $FH;
  400. delete $template->{rendered};
  401. return @lines;
  402. }