Something went wrong on our end
-
Stein Magne Bjorklund authored
Do not implement properties or code. It is a interface and should be typed as one.
Stein Magne Bjorklund authoredDo not implement properties or code. It is a interface and should be typed as one.
AdminModule.php 40.63 KiB
<?php declare(strict_types=1);
namespace uib\ub\loadspeakr\modules;
use ARC2;
use uib\ub\loadspeakr\Endpoint;
use uib\ub\loadspeakr\HTTPStatus;
use uib\ub\loadspeakr\Utils;
class AdminModule implements ModuleInterface
{
//Service module
private $head = "<!DOCTYPE html>
<html lang='en'>
<head>
<meta charset='utf-8'>
<title>LODSPeaKr Admin Menu</title>
<meta name='viewport' content='width=device-width, initial-scale=1.0'>
<meta name='description' content=''>
<meta name='author' content=''>
<link href='../css/bootstrap.min.css' rel='stylesheet' type='text/css' media='screen' />
<link href='codemirror/lib/codemirror.css' rel='stylesheet' type='text/css' media='screen' />
<style>
body {
padding-top: 60px; /* 60px to make the container go all the way to the bottom of the topbar */
}
.CodeMirror {border: 1px solid black;}
.cm-mustache {color: #0ca;}
.wait{
background-image:url('img/wait.gif');
background-repeat:no-repeat;
padding-right:20px;
background-position: right;
}
.strong{font-weight: 900; font-size:120%}
.cheat-sheet{
-moz-border-radius: 15px;
-webkit-border-radius: 15px;
border-radius: 15px;
min-height: 200px;
background:lightgray;
width:400px;
padding:5px;
position:absolute;
border:1px solid black;
right:-370px;
top:120px;
opacity:0.9
}
.cheat-title{
writing-mode:tb-rl;
-webkit-transform:rotate(90deg);
-moz-transform:rotate(90deg);
-o-transform: rotate(90deg);
white-space:nowrap;
display:block;
width:20px;
height:40px;
font-size:24px;
font-weight:normal;
text-shadow: 0px 0px 1px #333;
}
.first-editor{
top:120px;
}
.second-editor{
top:540px;
}
/* Base class */
.bs-docs-template {
position: relative;
margin: 0px 0;
padding: 39px 19px 14px;
*padding-top: 19px;
background-color: #fff;
border: 1px solid #ddd;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
.cheat-list{
margin-left:60px;
margin-top:-30px;
}
.sparql-list{
margin-left:60px;
margin-top:-40px;
}
/* Echo out a label for the example */
.bs-docs-template:after {
content: 'Template';
position: absolute;
top: -1px;
left: -1px;
padding: 3px 7px;
font-size: 12px;
font-weight: bold;
background-color: #f5f5f5;
border: 1px solid #ddd;
color: #9da0a4;
-webkit-border-radius: 4px 0 4px 0;
-moz-border-radius: 4px 0 4px 0;
border-radius: 4px 0 4px 0;
}
.bs-docs-query {
position: relative;
margin: 0px 0;
padding: 39px 19px 14px;
*padding-top: 19px;
background-color: #fff;
border: 1px solid #ddd;
-webkit-border-radius: 4px;
-moz-border-radius: 4px;
border-radius: 4px;
}
/* Echo out a label for the example */
.bs-docs-query:after {
content: 'Query';
position: absolute;
top: -1px;
left: -1px;
padding: 3px 7px;
font-size: 12px;
font-weight: bold;
background-color: #f5f5f5;
border: 1px solid #ddd;
color: #9da0a4;
-webkit-border-radius: 4px 0 4px 0;
-moz-border-radius: 4px 0 4px 0;
border-radius: 4px 0 4px 0;
}
.cheat-example{
background:#ccc;
font-family:monaco;
font-size:11px;
}
.embed-code{
font-family:Monaco;
}
</style>
<link href='../css/bootstrap-responsive.min.css' rel='stylesheet' type='text/css' media='screen' />
<script type='text/javascript' src='../js/jquery.js'></script>
<script type='text/javascript' src='../js/bootstrap.min.js'></script>
</head>
<body>
<div class='navbar navbar-fixed-top'>
<div class='navbar-inner'>
<div class='container'>
<a class='btn btn-navbar' data-toggle='collapse' data-target='.nav-collapse'>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
<span class='icon-bar'></span>
</a>
<a class='brand' href='../admin'>LODSPeaKr menu</a>
<div class='nav-collapse'>
<ul class='nav'>
<!--li class='dropdown'>
<a class='dropdown-toggle' data-toggle='dropdown' href='#'>SPARQL Endpoint<b class='caret'></b></a>
<ul class='dropdown-menu'>
<li><a href='../admin/start'>Start endpoint</a></li>
<li><a href='../admin/stop'>Stop endpoint</a></li>
<!--li><a href='../admin/load'>Add RDF</a></li>
<li><a href='../admin/remove'>Remove RDF</a></li>
</ul>
</li-->
<li>
<a class='dropdown-toggle' data-toggle='dropdown' href='../admin/namespaces'>Namespaces<b class='caret'></b></a>
</li>
<li>
<a class='dropdown-toggle' data-toggle='dropdown' href='../admin/endpoints'>Endpoints<b class='caret'></b></a>
</li>
<li>
<a class='dropdown-toggle' data-toggle='dropdown' href='../admin/components'>Component Editor<b class='caret'></b></a>
</li>
<li>
<a href='../'><i class='icon-share icon-white'></i> Go to main site</a>
</li>
</ul>
</div><!--/.nav-collapse -->
</div>
</div>
</div>
<div class='container'>
<img src='../img/lodspeakr_logotype.png' style='opacity: 0.1; position: absolute; right:0px; top:60%'/>
";
private $foot = " <div id='embed-box' class='modal hide fade'>
<div class='modal-header'>
<button type='button' class='close' data-dismiss='modal' aria-hidden='true'>×</button>
<h3>Embed this code</h3>
</div>
<div class='modal-body'>
<form class='form-inline'><fieldset><label>Width:</label> <input type='text' class='input-small embed-size' id='embed-width' value='600px'/> <label>Height:</label> <input type='text' class='input-small embed-size' id='embed-height' value='400px'/></fieldset></form>
<div id='embed-body' class='embed-code'>
</div>
<hr/>
<span id='tweet-span'></span>
<div class='fb-like' data-href='http://google.com' data-send='true' data-layout='button_count' data-width='450' data-show-faces='true' data-action='recommend'></div><div id='fb-root'></div>
<div class='modal-footer'>
<a href='#' class='btn' data-dismiss='modal'>Close</a>
</div>
</div></div>
</body>
</html>
";
public function match($uri)
{
global $localUri;
global $conf;
//URLs used by this component. Other URLs starting with admin/* won't be considered by this module
$operations = array(
"menu", /*"load", "remove",*/
"endpoints",
"namespaces",
"components",
""
);
$q = preg_replace('|^' . $conf['basedir'] . '|', '', $localUri);
$qArr = explode('/', $q);
if (sizeof($qArr) == 0) {
return false;
}
if ($qArr[0] == "admin" && array_search($qArr[1], $operations) !== false) {
if ($conf['admin']['pass'] !== false && !$this->auth()) {
HTTPStatus::send401("Forbidden\n");
exit(0);
}
return $qArr;
}
return false;
}
public function execute($params)
{
global $conf;
global $localUri;
global $uri;
global $acceptContentType;
global $endpoints;
global $lodspk;
global $firstResults;
if (sizeof($params) == 1) {
header('Location: admin/menu');
exit(0);
}
if ($params[1] == "") {
header('Location: menu');
exit(0);
}
switch ($params[1]) {
case "menu":
$this->homeMenu();
break;
/*case "start":
$this->startEndpoint();
break;
case "stop":
$this->stopEndpoint();
break;*/
/*case "load":
$this->loadRDF();
break;
case "remove":
$this->deleteRDF();
break;*/
case "namespaces":
$this->editNamespaces();
break;
case "endpoints":
$this->editEndpoints();
break;
case "components":
if (sizeof($params) == 2) {
$this->componentEditor();
} else {
$this->componentEditorApi(array_slice($params, 2));
}
break;
default:
HTTPStatus::send404($params[1]);
}
exit(0);
}
protected function loadRDF()
{
global $conf;
if (!isset($conf['updateendpoint']['local'])) {
echo $this->head . "
<div class='fluid-row'>
<div class='span8'>
<div class='alert alert-error'><strong>Error:</strong> No SPARQL/UPDATE server found. Please include it in <code>\$conf['updateendpoint']['local']</code> at <strong>settings.inc.php</strong></div>
</div>
</div>
" . $this->foot;
} else {
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
echo $this->head . "
<div class='fluid-row'>
<div class='span5'>
<form action='load' method='post'
enctype='multipart/form-data'>
<legend>Load RDF into the endpoint</legend>
<div class='alert alert-info'><span class='label label-info'>Important</span> If you load data into an existing Named graph, the content will be overwritten!</div>
<label for='file'>RDF file</label>
<input type='file' name='file' id='file' />
<span class='help-block'>LODSPeaKr accepts RDF/XML, Turtle and N-Triples files</span>
<label for='file'>Named graph</label>
<input type='text' name='namedgraph' id='namedgraph' value='default'/>
<span class='help-block'>The named graph where the RDF will be stored (optional).</span>
<br />
<button type='submit' class='btn btn-large'>Submit</button>
</form>
</div>
<div class='span6'>
<legend>Named Graphs</legend>
<div id='ng'></div>
</div>
</div>
<script type='text/javascript' src='" . $conf['basedir'] . "js/jquery.js'></script>
<script type='text/javascript' src='" . $conf['basedir'] . "js/namedgraphs.js'></script>
" . $this->foot;
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
if ($_FILES["file"]["error"] > 0) {
HTTPStatus::send409("No file was included in the request");
} else {
$ng = (isset($_POST['namedgraph'])) ? $_POST['namedgraph'] : 'default';
$parser = ARC2::getRDFParser();
$parser->parse($_FILES["file"]["tmp_name"]);
$triples = $parser->getTriples();
if (sizeof($triples) > 0) {
$c = curl_init();
$body = $parser->toTurtle($triples);
$fp = fopen('php://temp/maxmemory:256000', 'w');
if (!$fp) {
die('could not open temp memory data');
}
fwrite($fp, $body);
fseek($fp, 0);
curl_setopt($c, CURLOPT_URL, $conf['updateendpoint']['local'] . "?graph=" . $ng);
curl_setopt($c, CURLOPT_CUSTOMREQUEST, "PUT");
curl_setopt($c, CURLOPT_PUT, 1);
curl_setopt($c, CURLOPT_BINARYTRANSFER, true);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt(
$c,
CURLOPT_HTTPHEADER,
array('X-HTTP-Method-Override: PUT', "Content-Type: text/turtle")
);
curl_setopt($c, CURLOPT_USERAGENT, "LODSPeaKr version " . $conf['version']);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_INFILE, $fp); // file pointer
curl_setopt($c, CURLOPT_INFILESIZE, strlen($body));
curl_exec($c); // execute the curl command
$http_status = curl_getinfo($c, CURLINFO_HTTP_CODE);
if (intval($http_status) >= 200 && intval($http_status) < 300) {
echo $this->head . "<h2>Success!!</h2><div class='alert alert-success'>The file " . $_FILES["file"]["name"] . " (" . $_FILES["file"]["size"] . " bytes, " . sizeof(
$triples
) . " triples) was stored successfully on $ng.</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
} else {
HTTPStatus::send502(
$this->head . "<h2>Error!!</h2><div class='alert alert-success'>The file " . $_FILES["file"]["name"] . " couldn't be loaded into the triples store. The server was acting as a gateway or proxy and received an invalid response (" . $http_status . ") from the upstream server</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot
);
}
curl_close($c);
fclose($fp);
} else {
HTTPStatus::send409(
$this->head . "<h2>Error!!</h2><div class='alert alert-error'>The file was not a valid RDF document.</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot
);
}
}
} else {
HTTPStatus::send405($_SERVER['REQUEST_METHOD']);
}
exit(0);
}
}
protected function deleteRDF()
{
global $conf;
if (!isset($conf['updateendpoint']['local'])) {
echo $this->head . "
<div class='fluid-row'>
<div class='span8'>
<div class='alert alert-error'><strong>Error:</strong> No SPARQL/UPDATE server found. Please include it in <code>\$conf['updateendpoint']['local']</code> at <strong>settings.inc.php</strong></div>
</div>
</div>
" . $this->foot;
} else {
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
echo $this->head . "
<div class='fluid-row'>
<div class='span5'>
<form action='remove' method='post'
enctype='multipart/form-data'>
<legend>Remove a Named Graph containing RDF</legend>
<label for='file'>Named graph</label>
<input type='text' name='namedgraph' id='namedgraph' />
<span class='help-block'>The named graph where the RDF is stored.</span>
<br />
<button type='submit' class='btn'>Submit</button></form>
</div>
<div class='span6'>
<legend>Named Graphs</legend>
<div id='ng'></div>
</div>
</div>
<script type='text/javascript' src='" . $conf['basedir'] . "js/jquery.js'></script>
<script type='text/javascript' src='" . $conf['basedir'] . "js/namedgraphs.js'></script>
" . $this->foot;
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
$ng = (isset($_POST['namedgraph'])) ? $_POST['namedgraph'] : 'default';
$c = curl_init();
curl_setopt($c, CURLOPT_URL, $conf['updateendpoint']['local'] . "?graph=" . $ng);
curl_setopt($c, CURLOPT_CUSTOMREQUEST, 'DELETE');
curl_setopt($c, CURLOPT_BINARYTRANSFER, true);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_setopt($c, CURLOPT_USERAGENT, "LODSPeaKr version " . $conf['version']);
curl_setopt($c, CURLOPT_RETURNTRANSFER, true);
curl_exec($c); // execute the curl command
$http_status = curl_getinfo($c, CURLINFO_HTTP_CODE);
if (intval($http_status) >= 200 && intval($http_status) < 300) {
echo $this->head . "<h2>Success!!</h2><div class='alert alert-success'>The named graph $ng was removed successfully</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
} else {
HTTPStatus::send502(
$this->head . "<h2>Error!!</h2><div class='alert alert-error'>The named graph $ng couldn't be removed from the endpoint. The server was acting as a gateway or proxy and received an invalid response (" . $http_status . ") from the upstream server</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot
);
}
curl_close($c);
} else {
HTTPStatus::send405($_SERVER['REQUEST_METHOD']);
}
}
exit(0);
}
protected function editNamespaces()
{
global $conf;
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$nstable = "";
foreach ($conf['ns'] as $k => $v) {
$nstable .= "<tr><td>" . $k . "</td><td id='$k'>" . $v . "</td><td><button class='button btn edit-button' data-prefix='$k' data-ns='$v'>Edit</button></tr>";
}
echo $this->head . "
<div class='fluid-row'>
<div class='span7'>
<form action='namespaces' method='post'
enctype='multipart/form-data'>
<legend>Edit main namespace</legend>
<label for='file'>Prefix</label>
<input type='text' name='prefix' id='prefix' value='local'/>
<span class='help-block'>The prefix to describe this namespace ('local' is the one used to mirror URIs of the data in this server)</span>
<label for='file'>Namespace</label>
<input type='text' name='namespace' id='namespace' value='" . $conf['ns']['local'] . "'/>
<span class='help-block'>The namespace of the data being served</span>
<br />
<button type='submit' class='btn'>Submit</button></form>
</div>
<div class='span4 well'>
<legend>Edit local namespace</legend>
<p>'local' namespace defines which types of URI will be mirrored in this server. Thus, it is possible to serve data about <code>http://example.org/myresource</code> by dereferencing <code>" . $conf['ns']['local'] . "myresource</code></p>
<legend>Add a new namespace</legend>
<p>To add a new namespace, simply change the prefix from 'local' to the new one you want to add and include the namespaces in the following box.</p>
<legend>Edit other namespace</legend>
<p>Click on 'edit' in the proper row in the following table and modify the values in the form.</p>
</div>
</div>
<script type='text/javascript' src='" . $conf['basedir'] . "js/jquery.js'></script>
<script type='text/javascript'>
//<![CDATA[
$(document).ready(function(){
$('.edit-button').on('click', function(target){
$('#prefix').val($(this).attr('data-prefix'));
$('#namespace').val($(this).attr('data-ns'));
$('html, body').stop().animate({
scrollTop: $('body').offset().top
}, 1000);
})
});
//]]>
</script>
<div class='fluid-row'>
<div class='span8'>
<legend>Edit other namespaces</legend>
<table class='table table-striped'>
<thead><td>Prefix</td><td>Namespace</td><td>Edit</td></thead>$nstable</table>
" . $this->foot;
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
$ns = (isset($_POST['namespace'])) ? $_POST['namespace'] : 'http://' . $_SERVER['SERVER_NAME'] . '/';
$prefix = (isset($_POST['prefix'])) ? $_POST['prefix'] : 'local';
$return_var = 0;
exec("php utils/modules/remove-namespace.php " . $prefix, $output, $return_var);
exec("php utils/modules/add-namespace.php " . $prefix . " " . $ns, $output, $return_var);
if ($return_var == 0) {
echo $this->head . "<div class='alert alert-success'>Your main namespace was updated successfully to $ns</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
} else {
echo $this->head . "<div class='alert alert-error'>Error: Update did not finished successfullt. Please check setting.inc.php located at " . $conf['home'] . ".</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
}
}
}
protected function editEndpoints()
{
global $conf;
if ($_SERVER['REQUEST_METHOD'] == 'GET') {
$nstable = "";
foreach ($conf['endpoint'] as $k => $v) {
$nstable .= "<tr><td>" . $k . "</td><td id='$k'>" . $v . "</td><td><button class='button btn edit-button' data-prefix='$k' data-ns='$v'>Edit</button></tr>";
}
echo $this->head . "
<div class='fluid-row'>
<div class='span7'>
<form action='endpoints' method='post'
enctype='multipart/form-data'>
<legend>Edit Endpoints</legend>
<label for='file'>Shortname</label>
<input type='text' name='prefix' id='prefix' value='local'/>
<span class='help-block'>The prefix to describe this namespace ('local' is the one used to mirror URIs of the data in this server)</span>
<label for='file'>Endpoint</label>
<input type='text' name='endpoint' id='endpoint' value='" . $conf['endpoint']['local'] . "'/>
<span class='help-block'>The endpoint URL</span>
<br />
<button type='submit' class='btn'>Submit</button></form>
</div>
<div class='span4 well'>
<legend>Add or edit an endpoint</legend>
<p>To add a new endpoint, simply add a new prefix, the SPARQL endpoint URL and click on Submit.</p>
<p>To edit an endpoint, click on 'edit' in the proper row in the following table and modify the values in the form.</p>
</div>
</div>
<script type='text/javascript' src='" . $conf['basedir'] . "js/jquery.js'></script>
<script type='text/javascript'>
//<![CDATA[
$(document).ready(function(){
$('.edit-button').on('click', function(target){
$('#prefix').val($(this).attr('data-prefix'));
$('#endpoint').val($(this).attr('data-ns'));
$('html, body').stop().animate({
scrollTop: $('body').offset().top
}, 1000);
})
});
//]]>
</script>
<div class='fluid-row'>
<div class='span8'>
<legend>Edit other namespaces</legend>
<table class='table table-striped'>
<thead><td>Prefix</td><td>Namespace</td><td>Edit</td></thead>$nstable</table>
" . $this->foot;
} elseif ($_SERVER['REQUEST_METHOD'] == 'POST') {
$ns = (isset($_POST['endpoint'])) ? $_POST['endpoint'] : 'http://' . $_SERVER['SERVER_NAME'] . '/';
$prefix = (isset($_POST['prefix'])) ? $_POST['prefix'] : 'local';
$return_var = 0;
exec("php utils/modules/remove-endpoint.php " . $prefix, $output, $return_var);
exec("php utils/modules/add-endpoint.php " . $prefix . " " . $ns, $output, $return_var);
if ($return_var == 0) {
echo $this->head . "<div class='alert alert-success'>Your endpoint was updated successfully to $ns</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
} else {
echo $this->head . "<div class='alert alert-error'>Error: Update did not finished successfully. Please check setting.inc.php located at " . $conf['home'] . ".</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
}
}
}
protected function startEndpoint()
{
$return_var = 0;
exec("utils/modules/start-endpoint.sh", $output, $return_var);
if ($return_var == 0) {
echo $this->head . "<div class='alert alert-success'>Endpoint starter successfully</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
} else {
echo $this->head . "<div class='alert alert-error'>Error: /tmp/fusekiPid already exists. This probably means Fuseki is already running. You could also try to <a href='stop'>stop</a> the endpoint first.</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
}
}
protected function componentEditor()
{
global $lodspk;
global $conf;
exec("utils/lodspk.sh list components", $output, $return_var);
$menu = "";
$endpointOptions = "";
foreach ($conf['endpoint'] as $k => $v) {
$selected = "";
if ($k == "local") {
$selected = 'selected';
}
$endpointOptions .= "<option $selected value='$k'>$k ($v)</option>";
}
$namespaces = "var ns = " . json_encode($conf['ns']);
$lastComponentType = "";
$onlyService = false;
foreach ($output as $line) {
if ($line == "") {
$menu .= "</ul>\n";
} else {
if (preg_match("/^\w/", $line)) {
$lastComponentType = trim($line);
$singleLastComponentType = preg_replace('/(.*)s$/', '\1', $lastComponentType);
$menu .= "<ul class='nav nav-list'>
<li class='nav-header'>" . $lastComponentType . " <button class='btn btn-mini btn-info new-button' style='float:right' data-type='$singleLastComponentType'>new</button></li>\n";
} else {
$componentName = trim($line);
$menu .= "<li class='component-li'> <button type='button' class='close hide lodspk-delete-component' data-component-type='$singleLastComponentType' data-component-name='$componentName' style='align:left'>x</button>
<a href='#$componentName' class='lodspk-component' data-component-type='$lastComponentType' data-component-name='$componentName'>" . $componentName . "</a></li>\n";
}
}
}
echo $this->head . "
<script type='application/javascript'>
var home='" . $conf['basedir'] . "';
$namespaces
</script>
<div class='row-fluid'>
<div class='span3 well'>$menu<div id='component-msg' class='alert hide'></div></div>
<div class='bs-docs-template span9'>
<textarea class='field span12' rows='8' cols='25' id='template-editor' name='template-editor'></textarea>
<button class='btn btn-info disabled' id='template-save-button' data-url=''>Save</button>
<div class='alert alert-success hide' id='template-msg'></div>
</div>
</div>
<div class='row-fluid'>
<div class='span3'>
<div class='container'>
<div class='row-fluid'>
<div class='span3 well'>
<legend>Views <!-- button class='btn btn-mini btn-info new-file-button hide new-file-button-view' data-component=''>new</button --></legend>
<ul class='nav nav-list' id='template-list'>
</ul>
</div>
</div>
<div class='row-fluid'>
<div class='span3 well'>
<legend>Models <button class='btn btn-mini btn-info new-file-button hide new-file-button-model' data-component=''>new</button></legend>
<ul class='nav nav-list' id='query-list'>
</ul>
</div>
</div>
<div class='row-fluid'>
<div class='span3'>
<p><a href='#' id='preview-button' class='hide'><button class='btn btn-success btn-large'>View component</button></a></p>
<p><button id='embed-button' class='btn btn-success btn-large hide'>Share component</button></p>
</div>
</div>
</div>
</div>
<div class='span9 bs-docs-query'>
<textarea class='field span12' rows='8' cols='25' id='query-editor'></textarea>
<button class='btn btn-info disabled' id='query-save-button' data-url=''>Save</button>
<select style='float:right' id='endpoint-list'>$endpointOptions</select>
<button class='btn btn-success' style='float:right; margin-right:20px' id='query-test-button'>Test this query against</button>
<div class='alert alert-success hide' id='query-msg'></div>
</div>
</div>
<div class='row-fluid'>
<div class='span12'>
<h2>Query results preview</h2>
<span class='alert alert-error hide' id='results-msg'></span>
<table class='table' id='results'></table>
<div style='height:300px'></div>
</div>
</div>
</div>
</div>
</div>
<script src='" . $conf['basedir'] . "admin/codemirror/lib/codemirror.js'></script>
<script src='" . $conf['basedir'] . "admin/codemirror/lib/util/overlay.js'></script>
<script src='" . $conf['basedir'] . "admin/codemirror/mode/xml/xml.js'></script>
<script src='" . $conf['basedir'] . "admin/codemirror/mode/sparql/sparql.js'></script>
<script src='" . $conf['basedir'] . "admin/js/editor.js'></script>
" . $this->foot;
}
protected function componentEditorApi($params)
{
switch ($params[0]) {
case "details":
$this->getComponentDetails(array_slice($params, 1));
break;
case "save":
if (sizeof($params) > 2) {
$this->saveComponent($params);
} else {
HTTPStatus::send404($params[1]);
}
break;
case "create":
if (sizeof($params) > 2) {
$this->createComponent($params);
} else {
HTTPStatus::send404($params[1]);
}
break;
case "delete":
if (sizeof($params) > 2) {
$this->deleteComponent($params);
} else {
HTTPStatus::send404($params[1]);
}
break;
case "add":
if (sizeof($params) > 2) {
$this->addFile($params);
} else {
HTTPStatus::send404($params[1]);
}
break;
case "remove":
if (sizeof($params) > 2) {
$this->deleteFile($params);
} else {
HTTPStatus::send404($params[1]);
}
break;
case "query":
$this->queryEndpoint($_POST);
break;
default:
HTTPStatus::send404($params[1]);
}
}
protected function queryEndpoint($data)
{
global $endpoints;
global $conf;
$query = $data['query'];
$endpoint = $data['endpoint'];
if (isset($endpoint) && isset($conf['endpoint'][$endpoint])) {
if (!isset($endpoints[$endpoint])) {
$e = new Endpoint($conf['endpoint'][$endpoint], $conf['endpoint']['config']);
} else {
$e = $endpoints[$endpoint];
}
$aux = $e->query($query, Utils::getResultsType($query));
header("Content-type: " . $data['format']);
$jaux = json_encode($aux);
if (isset($jaux)) {
echo $jaux;
} else {
echo $aux;
HTTPStatus::send404($params[1]);
}
} else {
echo "no endpoint";
HTTPStatus::send404($params[1]);
}
}
protected function getComponentDetails($params)
{
$componentType = $params[0];
$componentName = $params[1];
if (!isset($componentType) || !isset($componentName)) {
HTTPStatus::send404();
exit(0);
}
$return_var = 0;
exec("utils/modules/detail-component.sh $componentType $componentName", $output, $return_var);
if ($return_var == 0) {
$comps = array();
$lastKey = "";
foreach ($output as $line) {
if ($line == "") {
$menu .= "</ul>\n";
} else {
if (preg_match("/^\w/", $line)) {
$lastKey = trim($line);
$comps[$lastKey] = array();
} else {
array_push($comps[$lastKey], trim($line));
}
}
}
header("Content-type: application/json");
echo json_encode($comps);
} else {
HTTPStatus::send500();
exit(0);
}
}
protected function saveComponent($params)
{
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
$path = implode("/", array_slice($params, 1));
if (file_exists("components/" . $path)) {
$result = file_put_contents("components/" . $path, $_POST['content']);
if ($result === false) {
HTTPStatus::send500();
} else {
echo json_encode(array('success' => true, 'size' => $result));
}
} else {
HTTPStatus::send500();
exit(0);
}
}
}
protected function createComponent($params)
{
$path = implode("/", array_slice($params, 1));
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (sizeof($params) != 3) {
HTTPStatus::send404();
exit(0);
}
$return_var = 0;
exec("utils/lodspk.sh create " . $params[1] . " " . $params[2], $output, $return_var);
//echo $return_var;exit(0);
if ($return_var !== 0) {
HTTPStatus::send500($params[0] . " " . $params[1]);
} else {
echo json_encode(array('success' => true, 'size' => $result));
}
} else {
HTTPStatus::send406();
exit(0);
}
}
protected function deleteComponent($params)
{
$path = implode("/", array_slice($params, 1));
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (sizeof($params) != 3) {
HTTPStatus::send404();
exit(0);
}
$return_var = 0;
exec("utils/lodspk.sh delete " . $params[1] . " " . $params[2], $output, $return_var);
if ($return_var !== 0) {
HTTPStatus::send500($params[0] . " " . $params[1]);
} else {
echo json_encode(array('success' => true, 'size' => $result));
}
} else {
HTTPStatus::send406();
exit(0);
}
}
protected function deleteFile($params)
{
$path = "components/" . implode("/", array_slice($params, 1));
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (sizeof($params) < 3) {
HTTPStatus::send404();
exit(0);
}
$return_var = 0;
if (strpos($path, "components") === 0 && strpos($path, '..') === false) {
exec("rm " . $path, $output, $return_var);
if ($return_var !== 0) {
echo json_encode(array('success' => false, path => $path));
} else {
echo json_encode(array('success' => true, path => $path));
}
} else {
HTTPStatus::send406();
exit(0);
}
} else {
echo json_encode(array('success' => false, path => $path));
}
}
protected function addFile($params)
{
$path = "components/" . implode("/", array_slice($params, 1));
$basicContent = "SELECT * WHERE{
?s ?p ?o
}LIMIT 10";
if (strpos($path, ".template") !== false) {
//It is not a query, but a template
$basicContent = "<!DOCTYPE html>
<html>
<head>
<meta http-equiv='Content-type' content='text/html; charset=utf-8'>
</head>
<body>
</body>
</html>";
}
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
if (sizeof($params) < 3) {
HTTPStatus::send404();
exit(0);
}
$return_var = 0;
if (file_exists($path)) {
echo json_encode(array('success' => false));
return;
}
$dirpath = $path;
$dirArray = explode("/", $path);
array_pop($dirArray);
$dirpath = implode("/", $dirArray);
if (!is_dir($dirpath)) {
$oldumask = umask(0);
$return_var = mkdir($dirpath, 0755, true);
umask($oldumask);
if ($return_var === false) {
HTTPStatus::send500("mkdir " . var_export($return_var, true) . " " . $dirpath);
}
}
$return_var = file_put_contents($path, $basicContent);
//echo $return_var;exit(0);
if ($return_var === false) {
HTTPStatus::send500("file_puts_content " . var_export($return_var, true) . " " . $path);
} else {
echo json_encode(array('success' => true, 'return' => $return_var));
}
} else {
HTTPStatus::send406();
exit(0);
}
}
protected function stopEndpoint()
{
$return_var = 0;
exec("utils/modules/stop-endpoint.sh", $output, $return_var);
if ($return_var == 0) {
echo $this->head . "<div class='alert alert-success'>Endpoint stopped successfully</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
} else {
echo $this->head . "<div class='alert alert-error'>Error: Something went wrong. Are you sure the endpoint is running?</div><div class='alert'>You can now return to the <a href='menu'>home menu</a>.</div>" . $this->foot;
}
}
protected function homeMenu()
{
global $conf;
$output = array();
echo $this->head . "
<div class='well span5'>
<h2>Components Editor</h2>
<p>You can create, remove and edit components (services types, etc) using the <a href='components'>editor</a></p>
<a href='components'><button class='btn btn-large btn-info'>Go to Editor</button></a>
</div>
<div class='span5 well'>
<h2>Options</h2>
<p>You can also:</p>
<ul>
<li>Add, remove or <a href='namespaces'>edit namespaces</a></li>
<li>Add, remove or <a href='endpoints'>edit endpoints</a></li>
</ul>
</div>
" . $this->foot;
}
protected function auth()
{
global $conf;
$realm = 'Restricted area';
//user => password
$users = array('admin' => $conf['admin']['pass']);
if (empty($_SERVER['PHP_AUTH_DIGEST'])) {
header('HTTP/1.1 401 Unauthorized');
header(
'WWW-Authenticate: Digest realm="' . $realm .
'",qop="auth",nonce="' . uniqid() . '",opaque="' . md5($realm) . '"'
);
die('Access to administration menu requires valid authentication');
}
// analyze the PHP_AUTH_DIGEST variable
if (!($data = $this->http_digest_parse($_SERVER['PHP_AUTH_DIGEST'])) ||
!isset($users[$data['username']])) {
return false;
}
//die('Wrong Credentials!');
// generate the valid response
$A1 = md5($data['username'] . ':' . $realm . ':' . $users[$data['username']]);
$A2 = md5($_SERVER['REQUEST_METHOD'] . ':' . $data['uri']);
$valid_response = md5(
$A1 . ':' . $data['nonce'] . ':' . $data['nc'] . ':' . $data['cnonce'] . ':' . $data['qop'] . ':' . $A2
);
if ($data['response'] != $valid_response) {
return false;
}
// die('Wrong Credentials!');
// ok, valid username & password
//echo 'You are logged in as: ' . $data['username'];
return true;
}
// function to parse the http auth header
protected function http_digest_parse($txt)
{
// protect against missing data
$needed_parts = array(
'nonce' => 1,
'nc' => 1,
'cnonce' => 1,
'qop' => 1,
'username' => 1,
'uri' => 1,
'response' => 1
);
$data = array();
$keys = implode('|', array_keys($needed_parts));
preg_match_all('@(' . $keys . ')=(?:([\'"])([^\2]+?)\2|([^\s,]+))@', $txt, $matches, PREG_SET_ORDER);
foreach ($matches as $m) {
$data[$m[1]] = $m[3] ? $m[3] : $m[4];
unset($needed_parts[$m[1]]);
}
return $needed_parts ? false : $data;
}
}