Skip to content
semsol edited this page Mar 14, 2011 · 2 revisions

Setup

Create an index.php file and put it in your wiki target directory:

/* ARC2 static class inclusion */ 
include_once('path/to/arc/ARC2.php');

/* configuration */ 
$config = array(
  // no config needed for now
);

/* instantiation */
$wiki = ARC2::getComponent('DataWikiPlugin', $config);

/* request handling */
$wiki->go();

Now you need a .htaccess file (or alternatively tweak the Apache configuration directly). If your server supports mod_headers, you can let Apache handle GET requests and send the "MS-Author-Via" Header:

# indicate SPARQL Update friendliness
Header set MS-Author-Via: SPARQL

# enable rewrites
RewriteEngine on
RewriteBase /path/to/wiki

# redirect POSTs to the Wiki script
RewriteCond %{REQUEST_METHOD} POST
RewriteRule !^index\.php index.php [L]

# redirect 404s to the Wiki script 
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule !^index\.php index.php [L]

Otherwise (w/o the mod_headers module), you have to route GET request through the PHP script, too:

# enable rewrites
RewriteEngine on
RewriteBase /path/to/wiki

# redirect requests to the Wiki script
RewriteRule !^index\.php index.php [L]

Place the .htaccess file in the wiki directory.

Make sure the index.php script has write privileges for the wiki directory.

Now you should be able to use a SPARQL-enabled RDF editor such as Tabulator to create and edit RDF documents.

Source

Save to "plugins/ARC2_DataWikiPlugin.php".

<?php
/*
homepage: http://arc.semsol.org/
license:  http://arc.semsol.org/license

class:    ARC2 Data Wiki Plugin
author:   Benjamin Nowack
version:  2008-01-14
*/

ARC2::inc('Class');

class ARC2_DataWikiPlugin extends ARC2_Class {

  function __construct($a = '', &$caller) {
    parent::__construct($a, $caller);
  }
  
  function ARC2_DataWikiPlugin($a = '', &$caller) {
    $this->__construct($a, $caller);
  }

  function __init() {
    parent::__init();
    $this->headers = array('http' => 'HTTP/1.1 200 OK');
  }

  /*  */

  function handleRequest() {
    $rel_path = $this->getRequestPath();
    if (!$rel_path) {
      $this->handleRootRequest();
    }
    elseif (preg_match('/^post$/i', $_SERVER['REQUEST_METHOD'])) {
      $this->handleUpdateRequest();
    }
    elseif (!file_exists($rel_path)) {
      $this->handle404Request();
    }
    else {
      $this->handlePassThroughRequest();
    }
  }

  function setHeader($k, $v) {
    $this->headers[$k] = $v;
  }
  
  function sendHeaders() {
    foreach ($this->headers as $k => $v) {
      header($v);
    }
  }
  
  function getResult() {
    return $this->result;
  }
  
  function go() {
    $this->handleRequest();
    $this->sendHeaders();
    echo $this->getResult();
  }

  /*  */
  
  function getAbsBase() {
    return preg_replace('/index\.php$/', '', ARC2::getScriptURI());
  }

  function getRelBase() {
    return preg_replace('/index\.php$/', '', $_SERVER["SCRIPT_NAME"]);
  }
    
  function getRequestPath() {
    $r = $_SERVER['REQUEST_URI'];
    $r = preg_match('/^[^\/][a-z0-9]+\:[\/]+[^\/]+(.*)$/i', $r, $m) ? $m[1] : $r;
    $r = substr($r, strlen($this->getRelBase()));
    if (!$r || ($r == 'index.php')) return '';
    $r = preg_replace('/\.[^\.]+$/', '', $r) . '.rdf';
    return $r;
  }
  
  function getRequestURI(){
    return $this->getAbsBase() . $this->getRequestPath();
  }
  
  function getTargetGraph() {
    $r = preg_replace('/[\#\?].*$/', '', $this->getRequestURI());
    $r = preg_replace('/\.rdf$/', '', $r);
    return $r;
  }
  
  /*  */
  
  function handleRootRequest() {
    $this->setHeader('content-type', 'Content-type: text/html; charset=utf-8');
    $this->result = $this->getWelcomeDoc();
  }
  
  function handle404Request() {
    $this->setHeader('content-type', 'Content-type: application/rdf+xml; charset=utf-8');
    $this->setHeader('author-via', 'MS-Author-Via: SPARQL');
    $this->result = '<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"></rdf:RDF>';
  }
  
  function handlePassThroughRequest() {
    $rel_path = $this->getRequestPath();
    $this->setHeader('content-type', 'Content-type: application/rdf+xml; charset=utf-8');
    $this->setHeader('author-via', 'MS-Author-Via: SPARQL');
    $this->result = file_get_contents($rel_path);
  }
  
  function handleUpdateRequest() {
    $this->result = '';
    $this->setHeader('http', 'HTTP/1.1 403 Forbidden');
    $rel_path = $this->getRequestPath();
    if ($q = @file_get_contents('php://input')) {
      $triples = array();
      if (file_exists($rel_path)) {
        $parser = ARC2::getRDFParser($this->a);
        $parser->parse($rel_path);
        $triples = $parser->getTriples();
      }
      $index = ARC2::getSimpleIndex($triples, 0);
      /* split combined INSERT/DELETE query */
      if (preg_match('/^\s*(DELETE.*)\s*(INSERT.*)$/is', $q, $m)) {
        $qs = array($m[1], $m[2]);
      }
      else {
        $qs = array($q);
      }
      $this->writeLog(print_r($qs, 1));
      foreach ($qs as $q) {
        $index = $this->getUpdatedIndex($index, $q);
        $this->writeLog(print_r($index, 1));
        if (!$this->getErrors()) {
          $this->setHeader('http', 'HTTP/1.1 200 OK');
          if ($index) {
            /* todo: create dirs, if necessary */
            $fp = fopen($rel_path, 'w');
            fwrite($fp, $this->toRDFXML($index));
            fclose($fp);
          }
          else {
            unlink($rel_path);
          }
        }
      }
    }
  }
  
  function getUpdatedIndex($old_index, $q) {
    if (!preg_match('/^\s*(INSERT|DELETE)\s*(INTO|FROM)?\s*(.*)$/is', $q, $m)) {
      return 0;
    }
    $qt = strtolower($m[1]);
    $g = $this->getTargetGraph();
    /* inject a target graph, if necessary */
    if (!$m[2]) {
      $q = strtoupper($qt) . (($qt == 'insert') ? ' INTO ' : ' FROM') . ' <' . $g . '> ' . $m[3];
    }
    /* parse the query */
    $this->writeLog($q);
    ARC2::inc('SPARQLPlusParser');
    $p = & new ARC2_SPARQLPlusParser($this->a, $this);
    $p->parse($q);
    $infos = $p->getQueryInfos();
    /* errors? */
    if ($errors = $this->getErrors()) {
      $this->setHeader('http', 'HTTP/1.1 400 Bad Request');
      $this->setHeader('content-type', 'Content-type: text/plain; charset=utf-8');
      $this->result = join("\n", $errors);
      return 0;
    }
    $q_index = ARC2::getSimpleIndex($infos['query']['construct_triples'], 0);
    if ($qt == 'insert') {
      return ARC2::getMergedIndex($old_index, $q_index);
    }
    elseif ($qt == 'delete') {
      return ARC2::getCleanedIndex($old_index, $q_index);
    }
  }
  
  /*  */

  function getWelcomeDoc() {
    if (file_exists('welcome.htm')) {
      return file_get_contents('welcome.htm');
    }
    else {
      return '<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
        <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
        	<head>
        		<title>ARC Data Wiki</title>
        		<style type="text/css">
              body {
                font-size: 14px;
              	font-family: Trebuchet MS, Verdana, Geneva, sans-serif;
                background-color: #fcfcfc;
                padding: 20px 20% 20px 20%;
                color: #666;
              }
              h1 { font-size: 1.6em; font-weight: normal; }
              a { color: #c00000; }
        		</style>
        	</head>
        	<body>
            <h1>ARC Data Wiki (v' . ARC2::getVersion() . ')</h1>
            <p>
              This Web space allows the manipulation of RDF documents via 
              <a href="https://github.com/semsol/arc2/wiki/SPARQL%2B">SPARQL+</a> 
              (or, more precisely, a slightly tweaked version of it which 
              accepts INSERT and DELETE queries without an explicitly specified target graph).
            </p>
            <p>
              <a href="https://github.com/semsol/arc2/wiki/Plugins:-Data-Wiki">Plugin Documentation</a>
            </p>
          </body>
        </html>
      ';
    }
  }

  /*  */

  function writeLog($v) {
    return 1;
    $fp = fopen('log.txt', 'a');
    $now = time();
    fwrite($fp, date('Y-m-d\TH:i:s\Z', $now) . ' : ' . $v . '' . "\r\n");
    fclose($fp);
  }

  /*  */

}