1<?php
2
3namespace dokuwiki\plugin\struct\test\action;
4
5use dokuwiki\ChangeLog\PageChangeLog;
6use dokuwiki\plugin\struct\meta;
7use dokuwiki\plugin\struct\test\mock\action_plugin_struct_edit;
8use dokuwiki\plugin\struct\test\mock\Assignments;
9use dokuwiki\plugin\struct\test\StructTest;
10use DOMWrap\Document;
11
12/**
13 * @group plugin_struct
14 * @group plugins
15 *
16 * @covers action_plugin_struct_revert
17 * @covers action_plugin_struct_edit
18 */
19class EditTest extends StructTest
20{
21
22    public function setUp(): void
23    {
24        parent::setUp();
25
26        $this->loadSchemaJSON('schema1');
27        $this->loadSchemaJSON('schema2', 'schema2int');
28        $this->saveData(
29            'page01',
30            'schema1',
31            [
32                'first' => 'first data',
33                'second' => ['second data', 'more data', 'even more'],
34                'third' => 'third data',
35                'fourth' => 'fourth data'
36            ],
37            time()
38        );
39    }
40
41    /**
42     * Check if a field has the correct value
43     *
44     * @param Document $pq
45     * @param string $schema
46     * @param string $name
47     * @param string $value
48     */
49    protected function checkField(Document $pq, $schema, $name, $value)
50    {
51        $this->assertEquals(
52            1,
53            $pq->find("span.label:contains('$name')")->count(),
54            "Field $schema.$name not found"
55        );
56        $this->assertEquals(
57            $value,
58            $pq->find("input[name='struct_schema_data[$schema][$name]']")->attr('value'),
59            "Field $schema.$name has wrong value"
60        );
61    }
62
63    public function test_createForm_storedData()
64    {
65        $edit = new action_plugin_struct_edit();
66        global $ID;
67        $ID = 'page01';
68        $test_html = $edit->createForm('schema1');
69
70        $doc = new Document();
71        $doc->html($test_html);
72        $this->assertEquals('schema1', $doc->find('legend')->text());
73        $this->checkField($doc, 'schema1', 'first', 'first data');
74        $this->checkField($doc, 'schema1', 'second', 'second data, more data, even more');
75        $this->checkField($doc, 'schema1', 'third', 'third data');
76        $this->checkField($doc, 'schema1', 'fourth', 'fourth data');
77    }
78
79    public function test_createForm_emptyData()
80    {
81        $edit = new action_plugin_struct_edit();
82        global $ID;
83        $ID = 'page02';
84        $test_html = $edit->createForm('schema1');
85
86        $doc = new Document();
87        $doc->html($test_html);
88        $this->assertEquals('schema1', $doc->find('legend')->text());
89        $this->checkField($doc, 'schema1', 'first', '');
90        $this->checkField($doc, 'schema1', 'second', '');
91        $this->checkField($doc, 'schema1', 'third', '');
92        $this->checkField($doc, 'schema1', 'fourth', '');
93    }
94
95    public function test_createForm_postData()
96    {
97        global $INPUT, $ID;
98        $ID = 'page01';
99        $structdata = [
100            'schema1' => [
101                'first' => 'first post data',
102                'second' => ['second post data', 'more post data', 'even more post data'],
103                'third' => 'third post data',
104                'fourth' => 'fourth post data'
105            ]
106        ];
107        $INPUT->set(action_plugin_struct_edit::getVAR(), $structdata);
108
109        $edit = new action_plugin_struct_edit();
110        $test_html = $edit->createForm('schema1');
111
112        $doc = new Document();
113        $doc->html($test_html);
114        $this->assertEquals('schema1', $doc->find('legend')->text());
115        $this->checkField($doc, 'schema1', 'first', 'first post data');
116        $this->checkField($doc, 'schema1', 'second', 'second post data, more post data, even more post data');
117        $this->checkField($doc, 'schema1', 'third', 'third post data');
118        $this->checkField($doc, 'schema1', 'fourth', 'fourth post data');
119    }
120
121    public function test_edit_page_wo_schema()
122    {
123        $page = 'test_edit_page_wo_schema';
124
125        $request = new \TestRequest();
126        $response = $request->get(['id' => $page, 'do' => 'edit'], '/doku.php');
127        $structElement = $response->queryHTML('.struct_entry_form');
128
129        $this->assertCount(1, $structElement);
130        $this->assertEquals('', $structElement->html());
131    }
132
133    public function test_edit_page_with_schema()
134    {
135        $page = 'test_edit_page_with_schema';
136        $assignment = Assignments::getInstance();
137        $schema = 'schema2';
138        $assignment->addPattern($page, $schema);
139
140        $request = new \TestRequest();
141        $response = $request->get(['id' => $page, 'do' => 'edit'], '/doku.php');
142        $test_html = trim($response->queryHTML('.struct_entry_form')->html());
143
144        $doc = new Document();
145        $doc->html($test_html);
146        $this->assertEquals('schema2', $doc->find('legend')->text());
147        $this->checkField($doc, 'schema2', 'afirst', '');
148        $this->checkField($doc, 'schema2', 'asecond', '');
149        $this->checkField($doc, 'schema2', 'athird', '');
150        $this->checkField($doc, 'schema2', 'afourth', '');
151    }
152
153    public function test_preview_page_invaliddata()
154    {
155        $page = 'test_preview_page_invaliddata';
156        $assignment = Assignments::getInstance();
157        $schema = 'schema2';
158        $assignment->addPattern($page, $schema);
159
160        $request = new \TestRequest();
161        $structData = [
162            $schema => [
163                'afirst' => 'foo',
164                'asecond' => 'bar, baz',
165                'athird' => 'foobar',
166                'afourth' => 'Eve'
167            ]
168        ];
169        $request->setPost('struct_schema_data', $structData);
170        $response = $request->post(['id' => $page, 'do' => 'preview']);
171        $expected_errormsg = sprintf($this->getLang('validation_prefix') . $this->getLang('Validation Exception Decimal needed'), 'afourth');
172        $actual_errormsg = $response->queryHTML('.error')->html();
173        $test_html = trim($response->queryHTML('.struct_entry_form')->html());
174
175        $this->assertEquals($expected_errormsg, $actual_errormsg, 'If there is invalid data, then there should be an error message.');
176
177        $doc = new Document();
178        $doc->html($test_html);
179        $this->assertEquals('schema2', $doc->find('legend')->text());
180        $this->checkField($doc, 'schema2', 'afirst', 'foo');
181        $this->checkField($doc, 'schema2', 'asecond', 'bar, baz');
182        $this->checkField($doc, 'schema2', 'athird', 'foobar');
183        $this->checkField($doc, 'schema2', 'afourth', 'Eve');
184    }
185
186    public function test_preview_page_validdata()
187    {
188        $page = 'test_preview_page_validdata';
189        $assignment = Assignments::getInstance();
190        $schema = 'schema2';
191        $assignment->addPattern($page, $schema);
192
193        $request = new \TestRequest();
194        $structData = [
195            $schema => [
196                'afirst' => 'foo',
197                'asecond' => 'bar, baz',
198                'athird' => 'foobar',
199                'afourth' => '42'
200            ]
201        ];
202        $request->setPost('struct_schema_data', $structData);
203        $response = $request->post(['id' => $page, 'do' => 'preview']);
204        $actual_errormsg = $response->queryHTML('.error')->get(0);
205        $this->assertNull($actual_errormsg, "If all data is valid, then there should be no error message.");
206
207        $test_html = trim($response->queryHTML('.struct_entry_form')->html());
208
209        $doc = new Document();
210        $doc->html($test_html);
211        $this->assertEquals('schema2', $doc->find('legend')->text());
212        $this->checkField($doc, 'schema2', 'afirst', 'foo');
213        $this->checkField($doc, 'schema2', 'asecond', 'bar, baz');
214        $this->checkField($doc, 'schema2', 'athird', 'foobar');
215        $this->checkField($doc, 'schema2', 'afourth', '42');
216    }
217
218    public function test_fail_saving_empty_page()
219    {
220        $page = 'test_fail_saving_empty_page';
221        $assignment = Assignments::getInstance();
222        $schema = 'schema2';
223        $assignment->addPattern($page, $schema);
224
225        $request = new \TestRequest();
226        $structData = [
227            $schema => [
228                'afirst' => 'foo',
229                'asecond' => 'bar, baz',
230                'athird' => 'foobar',
231                'afourth' => '42'
232            ]
233        ];
234        $request->setPost('struct_schema_data', $structData);
235        $request->setPost('summary', 'only struct data saved');
236        $response = $request->post(['id' => $page, 'do' => 'save'], '/doku.php');
237        $expected_errormsg = $this->getLang('emptypage');
238        $actual_errormsg = $response->queryHTML('.error')->html();
239        $pagelog = new PageChangelog($page);
240        $revisions = $pagelog->getRevisions(-1, 200);
241
242        $this->assertCount(0, $revisions);
243        $this->assertEquals($expected_errormsg, $actual_errormsg, "An empty page should not be saved.");
244    }
245
246    public function test_fail_saveing_page_with_invaliddata()
247    {
248        $page = 'test_fail_saveing_page_with_invaliddata';
249        $assignment = Assignments::getInstance();
250        $schema = 'schema2';
251        $assignment->addPattern($page, $schema);
252
253        $wikitext = 'teststring';
254        $request = new \TestRequest();
255        $structData = [
256            $schema => [
257                'afirst' => 'foo',
258                'asecond' => 'bar, baz',
259                'athird' => 'foobar',
260                'afourth' => 'Eve'
261            ]
262        ];
263        $request->setPost('struct_schema_data', $structData);
264        $request->setPost('wikitext', $wikitext);
265        $request->setPost('summary', 'content and struct data saved');
266        $response = $request->post(['id' => $page, 'do' => 'save']);
267        $actual_wikitext = trim($response->queryHTML('#wiki__text')->html());
268        $expected_wikitext = $wikitext;
269
270        $actual_errormsg = $response->queryHTML('.error')->html();
271        $expected_errormsg = sprintf($this->getLang('validation_prefix') . $this->getLang('Validation Exception Decimal needed'), 'afourth');
272
273        $test_html = trim($response->queryHTML('.struct_entry_form')->html());
274
275        $pagelog = new PageChangelog($page);
276        $revisions = $pagelog->getRevisions(-1, 200);
277
278        // assert
279        $this->assertCount(0, $revisions);
280        $this->assertEquals($expected_errormsg, $actual_errormsg, 'If there is invalid data, then there should be an error message.');
281        $this->assertEquals($expected_wikitext, $actual_wikitext);
282
283        $doc = new Document();
284        $doc->html($test_html);
285        $this->assertEquals('schema2', $doc->find('legend')->text());
286        $this->checkField($doc, 'schema2', 'afirst', 'foo');
287        $this->checkField($doc, 'schema2', 'asecond', 'bar, baz');
288        $this->checkField($doc, 'schema2', 'athird', 'foobar');
289        $this->checkField($doc, 'schema2', 'afourth', 'Eve');
290
291        // todo: assert that no struct data has been saved
292    }
293
294    public function test_save_page()
295    {
296        $page = 'test_save_page';
297        $assignment = Assignments::getInstance();
298        $schema = 'schema2';
299        $assignment->addPattern($page, $schema);
300
301        $request = new \TestRequest();
302        $structData = [
303            $schema => [
304                'afirst' => 'foo',
305                'asecond' => 'bar, baz',
306                'athird' => 'foobar',
307                'afourth' => '42'
308            ]
309        ];
310        $request->setPost('struct_schema_data', $structData);
311        $request->setPost('wikitext', 'teststring');
312        $request->setPost('summary', 'content and struct data saved');
313        $request->post(['id' => $page, 'do' => 'save']);
314
315        $pagelog = new PageChangelog($page);
316        $revisions = $pagelog->getRevisions(-1, 200);
317        $revinfo = $pagelog->getRevisionInfo($revisions[0]);
318        $schemaData = meta\AccessTable::getPageAccess($schema, $page, $revisions[0]);
319        $actual_struct_data = $schemaData->getDataArray();
320        $expected_struct_data = [
321            'afirst' => 'foo',
322            'asecond' => ['bar', 'baz'],
323            'athird' => 'foobar',
324            'afourth' => 42
325        ];
326
327        $this->assertCount(1, $revisions);
328        $this->assertEquals('content and struct data saved', $revinfo['sum']);
329        $this->assertEquals(DOKU_CHANGE_TYPE_CREATE, $revinfo['type']);
330        $this->assertEquals($expected_struct_data, $actual_struct_data);
331        // todo: assert that pagerevision and struct data have the same timestamp
332    }
333
334    /**
335     * @group slow
336     */
337    public function test_save_page_without_new_text()
338    {
339        $page = 'test_save_page_without_new_text';
340        $assignment = Assignments::getInstance();
341        $schema = 'schema2';
342        $assignment->addPattern($page, $schema);
343        $wikitext = 'teststring';
344
345        // first save;
346        $request = new \TestRequest();
347        $structData = [
348            $schema => [
349                'afirst' => 'foo',
350                'asecond' => 'bar, baz',
351                'athird' => 'foobar',
352                'afourth' => '42'
353            ]
354        ];
355        $request->setPost('struct_schema_data', $structData);
356        $request->setPost('wikitext', $wikitext);
357        $request->setPost('summary', 'content and struct data saved');
358        $request->post(['id' => $page, 'do' => 'save']);
359
360        $this->waitForTick(true);
361
362        // second save - only struct data
363        $request = new \TestRequest();
364        $structData = [
365            $schema => [
366                'afirst' => 'foo2',
367                'asecond' => 'bar2, baz2',
368                'athird' => 'foobar2',
369                'afourth' => '43'
370            ]
371        ];
372        $request->setPost('struct_schema_data', $structData);
373        $request->setPost('wikitext', $wikitext);
374        $request->setPost('summary', '2nd revision');
375        $request->post(['id' => $page, 'do' => 'save']);
376
377        // assert
378        $pagelog = new PageChangelog($page);
379        $revisions = $pagelog->getRevisions(-1, 200);
380        $revinfo = $pagelog->getRevisionInfo($revisions[0]);
381        $schemaData = meta\AccessTable::getPageAccess($schema, $page, $revisions[0]);
382        $actual_struct_data = $schemaData->getDataArray();
383        $expected_struct_data = [
384            'afirst' => 'foo2',
385            'asecond' => ['bar2', 'baz2'],
386            'athird' => 'foobar2',
387            'afourth' => 43
388        ];
389
390        $this->assertCount(2, $revisions, 'there should be 2 (two) revisions');
391        $this->assertEquals('2nd revision', $revinfo['sum']);
392        $this->assertEquals(DOKU_CHANGE_TYPE_EDIT, $revinfo['type']);
393        $this->assertEquals($expected_struct_data, $actual_struct_data);
394        // todo: assert that pagerevisions and struct entries have the same timestamps
395    }
396
397    /**
398     * @group slow
399     */
400    public function test_delete_page()
401    {
402        $page = 'test_delete_page';
403        $assignment = Assignments::getInstance();
404        $schema = 'schema2';
405        $assignment->addPattern($page, $schema);
406        $wikitext = 'teststring';
407
408        // first save;
409        $request = new \TestRequest();
410        $structData = [
411            $schema => [
412                'afirst' => 'foo',
413                'asecond' => 'bar, baz',
414                'athird' => 'foobar',
415                'afourth' => '42'
416            ]
417        ];
418        $request->setPost('struct_schema_data', $structData);
419        $request->setPost('wikitext', $wikitext);
420        $request->setPost('summary', 'content and struct data saved');
421        $request->post(['id' => $page, 'do' => 'save']);
422
423        $this->waitForTick(true);
424
425        // delete
426        $request = new \TestRequest();
427        $structData = [
428            $schema => [
429                'afirst' => 'foo2',
430                'asecond' => 'bar2, baz2',
431                'athird' => 'foobar2',
432                'afourth' => '43'
433            ]
434        ];
435        $request->setPost('struct_schema_data', $structData);
436        $request->setPost('wikitext', '');
437        $request->setPost('summary', 'delete page');
438        $request->post(['id' => $page, 'do' => 'save']);
439
440        // assert
441        $pagelog = new PageChangelog($page);
442        $revisions = $pagelog->getRevisions(-1, 200);
443        $revinfo = $pagelog->getRevisionInfo($revisions[0]);
444        $schemaData = meta\AccessTable::getPageAccess($schema, $page, $revisions[0]);
445        $actual_struct_data = $schemaData->getDataArray();
446        $expected_struct_data = [
447            'afirst' => '',
448            'asecond' => [],
449            'athird' => '',
450            'afourth' => ''
451        ];
452
453        $this->assertCount(2, $revisions, 'there should be 2 (two) revisions');
454        $this->assertEquals('delete page', $revinfo['sum']);
455        $this->assertEquals(DOKU_CHANGE_TYPE_DELETE, $revinfo['type']);
456        $this->assertEquals($expected_struct_data, $actual_struct_data);
457        // todo: timestamps?
458    }
459
460    /**
461     * @group slow
462     */
463    public function test_revert_page()
464    {
465        $page = 'test_revert_page';
466        $assignment = Assignments::getInstance();
467        $schema = 'schema2';
468        $assignment->addPattern($page, $schema);
469        $wikitext = 'teststring';
470
471        global $conf;
472        $conf['useacl'] = 1;
473        $conf['superuser'] = 'admin';
474        $_SERVER['REMOTE_USER'] = 'admin'; //now it's testing as admin
475        global $default_server_vars;
476        $default_server_vars['REMOTE_USER'] = 'admin';  //Hack until Issue #1099 is fixed
477        global $USERINFO;
478        $USERINFO['name'] = 'admin';
479        $USERINFO['mail'] = 'admin@example.com';
480        $USERINFO['grps'] = ['admin', 'user'];
481
482        // first save;
483        $request = new \TestRequest();
484        $structData = [
485            $schema => [
486                'afirst' => 'foo',
487                'asecond' => 'bar, baz',
488                'athird' => 'foobar',
489                'afourth' => '42'
490            ]
491        ];
492        $request->setPost('struct_schema_data', $structData);
493        $request->setPost('wikitext', $wikitext);
494        $request->setPost('summary', 'content and struct data saved');
495        $request->post(['id' => $page, 'do' => 'save', 'sectok' => getSecurityToken()]);
496
497        $this->waitForTick(true);
498
499        // second save
500        $request = new \TestRequest();
501        $structData = [
502            $schema => [
503                'afirst' => 'foo2',
504                'asecond' => 'bar2, baz2',
505                'athird' => 'foobar2',
506                'afourth' => '43'
507            ]
508        ];
509        $request->setPost('struct_schema_data', $structData);
510        $request->setPost('wikitext', $wikitext . $wikitext);
511        $request->setPost('summary', 'delete page');
512        $request->post(['id' => $page, 'do' => 'save', 'sectok' => getSecurityToken()]);
513
514        $this->waitForTick(true);
515
516        // revert to first save
517        $actpagelog = new PageChangelog($page);
518        $actrevisions = $actpagelog->getRevisions(0, 200);
519
520        $actrevinfo = $actpagelog->getRevisionInfo($actrevisions[0]);
521        $request = new \TestRequest();
522        $request->setPost('summary', 'revert page');
523        $request->post(['id' => $page, 'do' => 'revert', 'rev' => $actrevinfo['date'], 'sectok' => getSecurityToken()]);
524
525        // assert
526        $pagelog = new PageChangelog($page);
527        $revisions = $pagelog->getRevisions(-1, 200);
528        $revinfo = $pagelog->getRevisionInfo($revisions[0]);
529        $schemaData = meta\AccessTable::getPageAccess($schema, $page, $revisions[0]);
530        $actual_struct_data = $schemaData->getDataArray();
531        $expected_struct_data = [
532            'afirst' => 'foo',
533            'asecond' => ['bar', 'baz'],
534            'athird' => 'foobar',
535            'afourth' => '42'
536        ];
537
538        $this->assertCount(3, $revisions, 'there should be 3 (three) revisions');
539        $this->assertStringContainsString('restored', $revinfo['sum']);
540        $this->assertEquals(DOKU_CHANGE_TYPE_REVERT, $revinfo['type']);
541        $this->assertEquals($expected_struct_data, $actual_struct_data);
542        // todo: timestamps?
543    }
544
545}
546