/**
  * Renames all occurances of a page ID in the database
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_move(Doku_Event $event, $param)
 {
     /** @var helper_plugin_struct_db $hlp */
     $hlp = plugin_load('helper', 'struct_db');
     $db = $hlp->getDB();
     if (!$db) {
         return false;
     }
     $old = $event->data['src_id'];
     $new = $event->data['dst_id'];
     // ALL data tables (we don't trust the assigments are still there)
     foreach (Schema::getAll() as $tbl) {
         $sql = "UPDATE data_{$tbl} SET pid = ? WHERE pid = ?";
         $db->query($sql, array($new, $old));
         $sql = "UPDATE multi_{$tbl} SET pid = ? WHERE pid = ?";
         $db->query($sql, array($new, $old));
     }
     // assignments
     $sql = "UPDATE schema_assignments SET pid = ? WHERE pid = ?";
     $db->query($sql, array($new, $old));
     // make sure assignments still match patterns;
     $assignments = new Assignments();
     $assignments->reevaluatePageAssignments($new);
     return true;
 }
 /**
  * Adds the structured data to the page body to be snippeted
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_snippets(Doku_Event $event, $param)
 {
     $id = $event->data['id'];
     $assignments = new Assignments();
     $tables = $assignments->getPageAssignments($id);
     if (!$tables) {
         return;
     }
     foreach ($tables as $table) {
         $schemadata = AccessTable::byTableName($table, $id, 0);
         $event->data['text'] .= $schemadata->getDataPseudoSyntax();
     }
 }
 /**
  * Enhance the editing form with structural data editing
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_editform(Doku_Event $event, $param)
 {
     global $ID;
     $assignments = new Assignments();
     $tables = $assignments->getPageAssignments($ID);
     $html = '';
     foreach ($tables as $table) {
         $html .= $this->createForm($table);
     }
     /** @var Doku_Form $form */
     $form = $event->data;
     $html = "<div class=\"struct_entry_form\">{$html}</div>";
     $pos = $form->findElementById('wiki__editbar');
     // insert the form before the main buttons
     $form->insertElement($pos, $html);
     return true;
 }
 /**
  * Save the data, by loading it from the old revision and storing it as a new revision
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_pagesave_after(Doku_Event $event, $param)
 {
     global $ACT;
     global $REV;
     if ($ACT != 'revert' || !$REV) {
         return false;
     }
     $assignments = new Assignments();
     //  we load the data to restore from DB:
     $tosave = $assignments->getPageAssignments($event->data['id']);
     foreach ($tosave as $table) {
         $accessOld = AccessTable::byTableName($table, $event->data['id'], $REV);
         $accessNew = AccessTable::byTableName($table, $event->data['id'], $event->data['newRevision']);
         $accessNew->saveData($accessOld->getDataArray());
         // make sure this schema is assigned
         $assignments->assignPageSchema($event->data['id'], $table);
     }
     return true;
 }
 public function test_diff()
 {
     $page = 'test_save_page_without_new_text';
     $assignment = new meta\Assignments();
     $schema = 'schema1';
     $assignment->addPattern($page, $schema);
     $wikitext = 'teststring';
     // first save;
     $request = new \TestRequest();
     $structData = array($schema => array('first' => 'foo', 'second' => 'bar, baz', 'third' => 'foobar', 'fourth' => '42'));
     $request->setPost('struct_schema_data', $structData);
     $request->setPost('wikitext', $wikitext);
     $request->setPost('summary', 'content and struct data saved');
     $request->post(array('id' => $page, 'do' => 'save'), '/doku.php');
     $this->waitForTick(true);
     // second save - only struct data
     $request = new \TestRequest();
     $structData = array($schema => array('first' => 'foo', 'second' => 'bar2, baz2', 'third' => 'foobar2', 'fourth' => '42'));
     $request->setPost('struct_schema_data', $structData);
     $request->setPost('wikitext', $wikitext);
     $request->setPost('summary', '2nd revision');
     $request->post(array('id' => $page, 'do' => 'save'), '/doku.php');
     // diff
     $request = new \TestRequest();
     $response = $request->post(array('id' => $page, 'do' => 'diff'), '/doku.php');
     $pq = $response->queryHTML('table.diff_sidebyside');
     $this->assertEquals(1, $pq->length);
     $added = $pq->find('td.diff-addedline');
     $deleted = $pq->find('td.diff-deletedline');
     $this->assertEquals(2, $added->length);
     $this->assertEquals(2, $deleted->length);
     $this->assertContains('bar', $deleted->eq(0)->html());
     $this->assertContains('baz', $deleted->eq(0)->html());
     $this->assertContains('bar2', $added->eq(0)->html());
     $this->assertContains('baz2', $added->eq(0)->html());
     $this->assertContains('foobar', $deleted->eq(1)->html());
     $this->assertContains('foobar2', $added->eq(1)->html());
 }
 /**
  * Add structured data to the diff
  *
  * This is done by adding pseudo syntax to the page source when it is loaded in diff context
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_diffload(Doku_Event $event, $param)
 {
     global $ACT;
     global $INFO;
     if ($ACT != 'diff') {
         return;
     }
     $id = $event->data[2];
     $rev = $event->data[3];
     if ($INFO['id'] != $id) {
         return;
     }
     $assignments = new Assignments();
     $tables = $assignments->getPageAssignments($id);
     if (!$tables) {
         return;
     }
     $event->result .= "\n---- struct data ----\n";
     foreach ($tables as $table) {
         $schemadata = AccessTable::byTableName($table, $id, $rev);
         $event->result .= $schemadata->getDataPseudoSyntax();
     }
     $event->result .= "----\n";
 }
 /**
  * Save the struct data
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_save(Doku_Event $event, $param)
 {
     // get all struct values and their associated schemas
     $tosave = array();
     foreach ($event->data['fields'] as $field) {
         if (!is_a($field, 'helper_plugin_struct_field')) {
             continue;
         }
         /** @var helper_plugin_struct_field $field */
         $tbl = $field->column->getTable();
         $lbl = $field->column->getLabel();
         if (!isset($tosave[$tbl])) {
             $tosave[$tbl] = array();
         }
         $tosave[$tbl][$lbl] = $field->getParam('value');
     }
     // save all the struct data of assigned schemas
     $id = $event->data['id'];
     $time = filemtime(wikiFN($id));
     $assignments = new Assignments();
     $assigned = $assignments->getPageAssignments($id);
     foreach ($tosave as $table => $data) {
         if (!in_array($table, $assigned)) {
             continue;
         }
         $access = AccessTable::byTableName($table, $id, $time);
         $validator = $access->getValidator($data);
         if ($validator->validate()) {
             $validator->saveData($time);
         }
     }
     return true;
 }
 /**
  * Returns all pages known to the struct plugin
  *
  * That means all pages that have or had once struct data saved
  *
  * @param string|null $schema limit the result to a given schema
  * @return array (page => (schema => true), ...)
  * @throws StructException
  */
 public function getPages($schema = null)
 {
     $assignments = new Assignments();
     return $assignments->getPages($schema);
 }
 /**
  * Render schema data
  *
  * Currently completely renderer agnostic
  *
  * @todo we currently have no schema headlines
  *
  * @param string $mode Renderer mode
  * @param Doku_Renderer $R The renderer
  * @param array $data The data from the handler() function
  * @return bool If rendering was successful.
  */
 public function render($mode, Doku_Renderer $R, $data)
 {
     global $ID;
     global $INFO;
     global $REV;
     if ($ID != $INFO['id']) {
         return true;
     }
     if (!$INFO['exists']) {
         return true;
     }
     if ($this->hasBeenRendered) {
         return true;
     }
     // do not render the output twice on the same page, e.g. when another page has been included
     $this->hasBeenRendered = true;
     $assignments = new Assignments();
     $tables = $assignments->getPageAssignments($ID);
     if (!$tables) {
         return true;
     }
     if ($mode == 'xhtml') {
         $R->doc .= '<div id="plugin__struct_output">';
     }
     foreach ($tables as $table) {
         $schemadata = AccessTable::byTableName($table, $ID, $REV);
         $schemadata->optionSkipEmpty(true);
         $data = $schemadata->getData();
         if (!count($data)) {
             continue;
         }
         $R->table_open();
         $R->tablethead_open();
         $R->tablerow_open();
         $R->tableheader_open(2);
         $R->cdata($table);
         $R->tableheader_close();
         $R->tablerow_close();
         $R->tablethead_open();
         $R->tabletbody_open();
         foreach ($data as $field) {
             $R->tablerow_open();
             $R->tableheader_open();
             $R->cdata($field->getColumn()->getTranslatedLabel());
             $R->tableheader_close();
             $R->tablecell_open();
             $field->render($R, $mode);
             $R->tablecell_close();
             $R->tablerow_close();
         }
         $R->tabletbody_close();
         $R->table_close();
     }
     if ($mode == 'xhtml') {
         $R->doc .= '</div>';
     }
     return true;
 }
 /**
  * Render HTML output, e.g. helpful text and a form
  */
 public function html()
 {
     global $ID;
     echo $this->locale_xhtml('assignments_intro');
     $ass = new Assignments();
     $assignments = $ass->getAllPatterns();
     echo '<form action="' . wl($ID) . '" action="post">';
     echo '<input type="hidden" name="do" value="admin" />';
     echo '<input type="hidden" name="page" value="struct_assignments" />';
     echo '<input type="hidden" name="sectok" value="' . getSecurityToken() . '" />';
     echo '<table class="inline">';
     // header
     echo '<tr>';
     echo '<th>' . $this->getLang('assign_assign') . '</th>';
     echo '<th>' . $this->getLang('assign_tbl') . '</th>';
     echo '<th></th>';
     echo '</tr>';
     // existing assignments
     foreach ($assignments as $assignment) {
         $schema = $assignment['tbl'];
         $assignee = $assignment['pattern'];
         $link = wl($ID, array('do' => 'admin', 'page' => 'struct_assignments', 'action' => 'delete', 'sectok' => getSecurityToken(), 'assignment[tbl]' => $schema, 'assignment[assign]' => $assignee));
         echo '<tr>';
         echo '<td>' . hsc($assignee) . '</td>';
         echo '<td>' . hsc($schema) . '</td>';
         echo '<td><a class="deleteSchema" href="' . $link . '">' . $this->getLang('assign_del') . '</a></td>';
         echo '</tr>';
     }
     // new assignment form
     echo '<tr>';
     echo '<td><input type="text" name="assignment[assign]" /></td>';
     echo '<td>';
     echo '<select name="assignment[tbl]">';
     foreach (Schema::getAll('page') as $table) {
         echo '<option value="' . hsc($table) . '">' . hsc($table) . '</option>';
     }
     echo '</select>';
     echo '</td>';
     echo '<td><button type="submit" name="action" value="add">' . $this->getLang('assign_add') . '</button></td>';
     echo '</tr>';
     echo '</table>';
 }
 /**
  * Saves struct data for given page and schema
  *
  * Please note that setting the $rev only influences the struct data timestamp,
  * not the page and changelog entries.
  *
  * @param string $page
  * @param string $schema
  * @param array $data
  * @param int $rev allows to override the revision timestamp
  */
 protected function saveData($page, $schema, $data, $rev = 0)
 {
     if (!$rev) {
         $rev = time();
     }
     saveWikiText($page, "test for {$page}", "saved for testing");
     $schemaData = AccessTable::byTableName($schema, $page, $rev);
     $schemaData->saveData($data);
     $assignments = new Assignments();
     $assignments->assignPageSchema($page, $schema);
 }
 /**
  * Save the data
  *
  * When this is called, INPUT data has been validated already.
  *
  * @param Doku_Event $event event object by reference
  * @param mixed $param [the parameters passed as fifth argument to register_hook() when this
  *                           handler was registered]
  * @return bool
  */
 public function handle_pagesave_after(Doku_Event $event, $param)
 {
     global $ACT;
     if ($ACT == 'revert') {
         return false;
     }
     // handled in revert
     $assignments = new Assignments();
     if ($event->data['changeType'] == DOKU_CHANGE_TYPE_DELETE && empty($GLOBALS['PLUGIN_MOVE_WORKING'])) {
         // clear all data on delete unless it's a move operation
         $tables = $assignments->getPageAssignments($event->data['id']);
         foreach ($tables as $table) {
             $schemaData = AccessTable::byTableName($table, $event->data['id'], time());
             if ($schemaData->getSchema()->isEditable()) {
                 $schemaData->clearData();
             }
         }
     } else {
         // save the provided data
         if ($this->tosave) {
             foreach ($this->tosave as $validation) {
                 if ($validation->getAccessTable()->getSchema()->isEditable()) {
                     $validation->saveData($event->data['newRevision']);
                     // make sure this schema is assigned
                     $assignments->assignPageSchema($event->data['id'], $validation->getAccessTable()->getSchema()->getTable());
                 }
             }
         }
     }
     return true;
 }
 /**
  * @group slow
  */
 public function test_revert_page()
 {
     $page = 'test_revert_page';
     $assignment = new meta\Assignments();
     $schema = 'Schema2';
     $assignment->addPattern($page, $schema);
     $wikitext = 'teststring';
     global $conf;
     $conf['useacl'] = 1;
     $conf['superuser'] = '******';
     $_SERVER['REMOTE_USER'] = '******';
     //now it's testing as admin
     global $default_server_vars;
     $default_server_vars['REMOTE_USER'] = '******';
     //Hack until Issue #1099 is fixed
     $USERINFO['grps'] = array('admin', 'user');
     // first save;
     $request = new \TestRequest();
     $structData = array($schema => array('afirst' => 'foo', 'asecond' => 'bar, baz', 'athird' => 'foobar', 'afourth' => '42'));
     $request->setPost('struct_schema_data', $structData);
     $request->setPost('wikitext', $wikitext);
     $request->setPost('summary', 'content and struct data saved');
     $request->post(array('id' => $page, 'do' => 'save', 'sectok' => getSecurityToken()), '/doku.php');
     $this->waitForTick(true);
     // second save
     $request = new \TestRequest();
     $structData = array($schema => array('afirst' => 'foo2', 'asecond' => 'bar2, baz2', 'athird' => 'foobar2', 'afourth' => '43'));
     $request->setPost('struct_schema_data', $structData);
     $request->setPost('wikitext', $wikitext . $wikitext);
     $request->setPost('summary', 'delete page');
     $request->post(array('id' => $page, 'do' => 'save', 'sectok' => getSecurityToken()), '/doku.php');
     $this->waitForTick(true);
     // revert to first save
     $actpagelog = new \PageChangelog($page);
     $actrevisions = $actpagelog->getRevisions(0, 200);
     $actrevinfo = $actpagelog->getRevisionInfo($actrevisions[0]);
     $request = new \TestRequest();
     $request->setPost('summary', 'revert page');
     $request->post(array('id' => $page, 'do' => 'revert', 'rev' => $actrevinfo['date'], 'sectok' => getSecurityToken()), '/doku.php');
     // assert
     $pagelog = new \PageChangelog($page);
     $revisions = $pagelog->getRevisions(-1, 200);
     $revinfo = $pagelog->getRevisionInfo($revisions[0]);
     $schemaData = meta\AccessTable::byTableName($schema, $page, 0);
     $actual_struct_data = $schemaData->getDataArray();
     $expected_struct_data = array('afirst' => 'foo', 'asecond' => array('bar', 'baz'), 'athird' => 'foobar', 'afourth' => '42');
     $this->assertEquals(3, count($revisions), 'there should be 3 (three) revisions');
     $this->assertContains('restored', $revinfo['sum']);
     $this->assertEquals(DOKU_CHANGE_TYPE_REVERT, $revinfo['type']);
     $this->assertEquals($expected_struct_data, $actual_struct_data);
     // todo: timestamps?
 }
 public function matchPagePattern($pattern, $page, $pns = null)
 {
     return parent::matchPagePattern($pattern, $page, $pns);
 }