<?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; } }