PageRenderTime 53ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 1ms

/application/controllers/ServiceController.php

https://code.google.com/p/ontowiki/
PHP | 1196 lines | 849 code | 165 blank | 182 comment | 170 complexity | 88a8d63d83c776e4585b842e7c136329 MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. <?php
  2. /**
  3. * This file is part of the {@link http://ontowiki.net OntoWiki} project.
  4. *
  5. * @copyright Copyright (c) 2011, {@link http://aksw.org AKSW}
  6. * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
  7. */
  8. /**
  9. * OntoWiki service controller.
  10. *
  11. * @package application
  12. * @subpackage mvc
  13. */
  14. class ServiceController extends Zend_Controller_Action
  15. {
  16. /** @var OntoWiki */
  17. protected $_owApp = null;
  18. /** @var Zend_Config */
  19. protected $_config = null;
  20. /**
  21. * Attempts an authentication to the underlying Erfurt framework via
  22. * HTTP GET/POST parameters.
  23. */
  24. public function authAction()
  25. {
  26. if (!$this->_config->service->allowGetAuth) {
  27. // disallow get
  28. if (!$this->_request->isPost()) {
  29. $this->_response->setRawHeader('HTTP/1.0 405 Method Not Allowed');
  30. $this->_response->setRawHeader('Allow: POST');
  31. exit();
  32. }
  33. }
  34. // fetch params
  35. if (isset($this->_request->logout)) {
  36. $logout = $this->_request->logout == 'true' ? true : false;
  37. } elseif (isset($this->_request->u)) {
  38. $username = $this->_request->u;
  39. $password = $this->_request->getParam('p', '');
  40. } else {
  41. $this->_response->setRawHeader('HTTP/1.0 400 Bad Request');
  42. // $this->_response->setRawHeader('');
  43. exit();
  44. }
  45. if ($logout) {
  46. // logout
  47. Erfurt_Auth::getInstance()->clearIdentity();
  48. session_destroy();
  49. $this->_response->setRawHeader('HTTP/1.0 200 OK');
  50. exit();
  51. } else {
  52. // authenticate
  53. $result = $owApp->erfurt->authenticate($username, $password);
  54. }
  55. // return HTTP result
  56. if ($result->isValid()) {
  57. // return success (200)
  58. $this->_response->setRawHeader('HTTP/1.0 200 OK');
  59. exit();
  60. } else {
  61. // return fail (401)
  62. $this->_response->setRawHeader('HTTP/1.0 401 Unauthorized');
  63. exit();
  64. }
  65. }
  66. /**
  67. * Entity search
  68. */
  69. public function entitiesAction()
  70. {
  71. $type = (string)$this->_request->getParam('type', 's');
  72. $match = (string)$this->_request->getParam('match');
  73. $type = $type[0]; // use only first letter
  74. if ($this->_owApp->selectedModel && strlen($match) > 2) {
  75. $namespaces = $this->_owApp->selectedModel->getNamespaces();
  76. $namespacesFlipped = array_flip($namespaces);
  77. $nsFilter = array();
  78. foreach ($namespacesFlipped as $prefix => $uri) {
  79. if (stripos($prefix, $match) === 0) {
  80. $nsFilter[] = 'FILTER (regex(str(?' . $type . '), "' . $uri . '"))';
  81. }
  82. }
  83. $store = $this->_owApp->selectedModel->getStore();
  84. $query = Erfurt_Sparql_SimpleQuery::initWithString(
  85. 'SELECT DISTINCT ?' . $type . '
  86. FROM <' . $this->_owApp->selectedModel->getModelIri() . '>
  87. WHERE {
  88. ?s ?p ?o.
  89. ' . implode(PHP_EOL, $nsFilter) . '
  90. }'
  91. );
  92. }
  93. }
  94. public function hierarchyAction()
  95. {
  96. $options = array();
  97. if (isset($this->_request->entry)) {
  98. $options['entry'] = $this->_request->entry;
  99. }
  100. $model = new OntoWiki_Model_Hierarchy(Erfurt_App::getInstance()->getStore(),
  101. $this->_owApp->selectedModel,
  102. $options);
  103. $this->view->open = true;
  104. $this->view->classes = $model->getHierarchy();
  105. $this->_response->setBody($this->view->render('partials/hierarchy_list.phtml'));
  106. // $this->_response->setBody(json_encode($model->getHierarchy()));
  107. }
  108. /**
  109. * Constructor
  110. */
  111. public function init()
  112. {
  113. // init controller variables
  114. $this->_owApp = OntoWiki::getInstance();
  115. $this->_config = $this->_owApp->config;
  116. $this->_session = $this->_owApp->session;
  117. // prepare Ajax context
  118. $ajaxContext = $this->_helper->getHelper('AjaxContext');
  119. $ajaxContext->addActionContext('view', 'html')
  120. ->addActionContext('form', 'html')
  121. ->addActionContext('process', 'json')
  122. ->initContext();
  123. }
  124. /**
  125. * Menu Action to generate JSON serializations of OntoWiki_Menu for context-, module-, component-menus
  126. */
  127. public function menuAction()
  128. {
  129. $module = $this->_request->getParam('module');
  130. $resource = $this->_request->getParam('resource');
  131. $translate = $this->_owApp->translate;
  132. // create empty menu first
  133. $menuRegistry = OntoWiki_Menu_Registry::getInstance();
  134. $menu = $menuRegistry->getMenu(EF_RDFS_RESOURCE);
  135. if (!empty($module)) {
  136. $moduleRegistry = OntoWiki_Module_Registry::getInstance();
  137. $menu = $moduleRegistry->getModule($module)->getContextMenu();
  138. }
  139. if (!empty($resource)) {
  140. $models = array_keys($this->_owApp->erfurt->getStore()->getAvailableModels(true));
  141. $isModel = in_array($resource, $models);
  142. $menu->prependEntry(
  143. 'Go to Resource (external)',
  144. (string)$resource
  145. );
  146. if ($this->_owApp->erfurt->getAc()->isModelAllowed('edit', $this->_owApp->selectedModel) ) {
  147. // Delete resource option
  148. $url = new OntoWiki_Url(
  149. array('controller' => 'resource', 'action' => 'delete'),
  150. array()
  151. );
  152. if ($isModel) {
  153. $url->setParam('m',$resource,false);
  154. }
  155. $url->setParam('r',$resource,true);
  156. $menu->prependEntry( 'Delete Resource', (string) $url );
  157. // edit resource option
  158. $menu->prependEntry('Edit Resource', 'javascript:editResourceFromURI(\''.(string) $resource.'\')');
  159. }
  160. // add resource menu entries
  161. $url = new OntoWiki_Url(
  162. array( 'action' => 'view'),
  163. array()
  164. );
  165. if ($isModel) {
  166. $url->setParam('m',$resource,false);
  167. }
  168. $url->setParam('r',$resource,true);
  169. $menu->prependEntry(
  170. 'View Resource',
  171. (string)$url
  172. );
  173. if ($isModel) {
  174. // add a seperator
  175. $menu->prependEntry(OntoWiki_Menu::SEPARATOR);
  176. // can user delete models?
  177. if ( $this->_owApp->erfurt->getAc()->isModelAllowed('edit', $resource) &&
  178. $this->_owApp->erfurt->getAc()->isActionAllowed('ModelManagement')
  179. ) {
  180. $url = new OntoWiki_Url(
  181. array('controller' => 'model', 'action' => 'delete'),
  182. array()
  183. );
  184. $url->setParam('m',$resource,false);
  185. $menu->prependEntry(
  186. 'Delete Knowledge Base',
  187. (string)$url
  188. );
  189. }
  190. // add entries for supported export formats
  191. foreach (array_reverse(Erfurt_Syntax_RdfSerializer::getSupportedFormats()) as $key => $format) {
  192. $url = new OntoWiki_Url(
  193. array('controller' => 'model', 'action' => 'export'),
  194. array()
  195. );
  196. $url->setParam('m',$resource,false);
  197. $url->setParam('f',$key);
  198. $menu->prependEntry(
  199. 'Export Knowledge Base as ' . $format,
  200. (string)$url
  201. );
  202. }
  203. // check if model could be edited (prefixes and data)
  204. if ($this->_owApp->erfurt->getAc()->isModelAllowed('edit', $resource)) {
  205. $url = new OntoWiki_Url(
  206. array('controller' => 'model', 'action' => 'add'),
  207. array()
  208. );
  209. $url->setParam('m',$resource,false);
  210. $menu->prependEntry(
  211. 'Add Data to Knowledge Base',
  212. (string)$url
  213. );
  214. $url = new OntoWiki_Url(
  215. array('controller' => 'model', 'action' => 'config'),
  216. array()
  217. );
  218. $url->setParam('m',$resource,false);
  219. $menu->prependEntry(
  220. 'Configure Knowledge Base',
  221. (string)$url
  222. );
  223. }
  224. // Select Knowledge Base
  225. $url = new OntoWiki_Url(
  226. array('controller' => 'model', 'action' => 'select'),
  227. array()
  228. );
  229. $url->setParam('m',$resource,false);
  230. $menu->prependEntry(
  231. 'Select Knowledge Base',
  232. (string)$url
  233. );
  234. } else {
  235. $query = Erfurt_Sparql_SimpleQuery::initWithString(
  236. 'SELECT *
  237. FROM <' . (string)$this->_owApp->selectedModel . '>
  238. WHERE {
  239. <' . $resource . '> a ?type .
  240. }'
  241. );
  242. $results[] = $this->_owApp->erfurt->getStore()->sparqlQuery($query);
  243. $query = Erfurt_Sparql_SimpleQuery::initWithString(
  244. 'SELECT *
  245. FROM <' . (string)$this->_owApp->selectedModel . '>
  246. WHERE {
  247. ?inst a <' . $resource . '> .
  248. } LIMIT 2'
  249. );
  250. if ( sizeof($this->_owApp->erfurt->getStore()->sparqlQuery($query)) > 0 ) {
  251. $hasInstances = true;
  252. } else {
  253. $hasInstances = false;
  254. }
  255. $typeArray = array();
  256. foreach ($results[0] as $row) {
  257. $typeArray[] = $row['type'];
  258. }
  259. if (in_array(EF_RDFS_CLASS, $typeArray) ||
  260. in_array(EF_OWL_CLASS, $typeArray) ||
  261. $hasInstances
  262. ) {
  263. // add a seperator
  264. $menu->prependEntry(OntoWiki_Menu::SEPARATOR);
  265. $url = new OntoWiki_Url(
  266. array('action' => 'list'),
  267. array()
  268. );
  269. $url->setParam('class',$resource,false);
  270. $url->setParam('init',"true",true);
  271. // add class menu entries
  272. if ($this->_owApp->erfurt->getAc()->isModelAllowed('edit', $this->_owApp->selectedModel) ) {
  273. $menu->prependEntry(
  274. 'Create Instance',
  275. "javascript:createInstanceFromClassURI('$resource');"
  276. );
  277. }
  278. $menu->prependEntry(
  279. 'List Instances',
  280. (string)$url
  281. );
  282. // ->prependEntry('Create Instance', $this->_config->urlBase . 'index/create/?r=')
  283. // ->prependEntry('Create Subclass', $this->_config->urlBase . 'index/create/?r=');
  284. }
  285. }
  286. }
  287. // Fire a event;
  288. $event = new Erfurt_Event('onCreateMenu');
  289. $event->menu = $menu;
  290. $event->resource = $resource;
  291. if (isset($isModel)) {
  292. $event->isModel = $isModel;
  293. }
  294. $event->model = $this->_owApp->selectedModel;
  295. $event->trigger();
  296. echo $menu->toJson();
  297. }
  298. public function preDispatch()
  299. {
  300. // disable auto-rendering
  301. $this->_helper->viewRenderer->setNoRender();
  302. // disable layout for Ajax requests
  303. $this->_helper->layout()->disableLayout();
  304. }
  305. public function sessionAction()
  306. {
  307. if (!isset($this->_request->name)) {
  308. throw new OntoWiki_Exception("Missing parameter 'name'.");
  309. exit;
  310. }
  311. if (isset($this->_request->namespace)) {
  312. $namespace = $this->_request->namespace;
  313. } else {
  314. $namespace = _OWSESSION;
  315. }
  316. $session = new Zend_Session_Namespace($namespace);
  317. $name = $this->_request->name;
  318. $method = 'set'; // default
  319. if (isset($this->_request->method)) {
  320. $method = $this->_request->method;
  321. }
  322. if (isset($this->_request->value)) {
  323. $value = $this->_request->value;
  324. } else if($method!='unsetArray' && $method!='unsetArrayKey' && !($method=='unset' && !is_array($session->$name))) {
  325. throw new OntoWiki_Exception('Missing parameter "value".');
  326. exit;
  327. }
  328. if (isset($this->_request->value) && isset($this->_request->valueIsSerialized) && $this->_request->valueIsSerialized == "true") {
  329. $value = json_decode(stripslashes($value), true);
  330. }
  331. if (isset($this->_request->key)) {
  332. $key = $this->_request->key;
  333. } else if ($method == 'setArrayValue' || $method == 'unsetArrayKey') {
  334. throw new OntoWiki_Exception('Missing parameter "key".');
  335. exit;
  336. }
  337. switch ($method) {
  338. case 'set':
  339. $session->$name = $value;
  340. break;
  341. case 'setArrayValue':
  342. if(!is_array($session->$name))$session->$name = array();
  343. $array = $session->$name;
  344. $array[$key] = $value;
  345. $session->$name = $array; //strange (because the __get and __set interceptors)
  346. break;
  347. case 'push':
  348. if (!is_array($session->$name)) {
  349. $session->$name = array();
  350. }
  351. array_push($session->$name, $value);
  352. break;
  353. case 'merge':
  354. if (!is_array($session->$name)) {
  355. $session->$name = array();
  356. }
  357. $session->$name = array_merge($session->$name, $value);
  358. break;
  359. case 'unset':
  360. // unset a value by inverting the array
  361. // and unsetting the specified key
  362. if (is_array($session->$name)) {
  363. $valuesAsKeys = array_flip($session->$name);
  364. unset($valuesAsKeys[$value]);
  365. $session->$name = array_flip($valuesAsKeys);
  366. } else {
  367. //unset a non-array
  368. unset($session->$name);
  369. }
  370. break;
  371. case 'unsetArrayKey':
  372. //done this way because of interceptor-methods...
  373. $new = array();
  374. if(is_array($session->$name)){
  375. foreach($session->$name as $comparekey => $comparevalue){
  376. if($comparekey != $key){
  377. $new[] = $comparevalue;
  378. }
  379. }
  380. }
  381. $session->$name = $new;
  382. break;
  383. case 'unsetArray':
  384. // unset the array
  385. // (the above unsets only values in arrays)
  386. unset($session->$name);
  387. break;
  388. }
  389. $msg = 'sessionStore: '
  390. . $name
  391. . ' = '
  392. . print_r($session->$name, true);
  393. $this->_owApp->logger->debug($msg);
  394. }
  395. /**
  396. * OntoWiki Sparql Endpoint
  397. *
  398. * Implements the SPARQL protocol according to {@link http://www.w3.org/TR/rdf-sparql-protocol/}.
  399. */
  400. public function sparqlAction()
  401. {
  402. // service controller needs no view renderer
  403. $this->_helper->viewRenderer->setNoRender();
  404. // disable layout for Ajax requests
  405. $this->_helper->layout()->disableLayout();
  406. $store = OntoWiki::getInstance()->erfurt->getStore();
  407. $response = $this->getResponse();
  408. // fetch params
  409. // TODO: support maxOccurs:unbound
  410. $queryString = $this->_request->getParam('query', '');
  411. if (get_magic_quotes_gpc()) {
  412. $queryString = stripslashes($queryString);
  413. }
  414. $defaultGraph = $this->_request->getParam('default-graph-uri', null);
  415. $namedGraph = $this->_request->getParam('named-graph-uri', null);
  416. if (!empty($queryString)) {
  417. $query = Erfurt_Sparql_SimpleQuery::initWithString($queryString);
  418. // overwrite query-specidfied dataset with protocoll-specified dataset
  419. if (null !== $defaultGraph) {
  420. $query->setFrom((array)$defaultGraph);
  421. }
  422. if (null !== $namedGraph) {
  423. $query->setFromNamed((array)$namedGraph);
  424. }
  425. // check graph availability
  426. $ac = Erfurt_App::getInstance()->getAc();
  427. foreach (array_merge($query->getFrom(), $query->getFromNamed()) as $graphUri) {
  428. if (!$ac->isModelAllowed('view', $graphUri)) {
  429. if (Erfurt_App::getInstance()->getAuth()->getIdentity()->isAnonymousUser()) {
  430. // In this case we allow the requesting party to authorize...
  431. $response->setRawHeader('HTTP/1.1 401 Unauthorized');
  432. $response->setHeader('WWW-Authenticate', 'Basic realm="OntoWiki"');
  433. $response->sendResponse();
  434. exit;
  435. } else {
  436. $response->setRawHeader('HTTP/1.1 500 Internal Server Error')
  437. ->setBody('QueryRequestRefused')
  438. ->sendResponse();
  439. exit;
  440. }
  441. }
  442. }
  443. $typeMapping = array(
  444. 'application/sparql-results+xml' => 'xml',
  445. 'application/json' => 'json',
  446. 'application/sparql-results+json' => 'json'
  447. );
  448. try {
  449. $type = OntoWiki_Utils::matchMimetypeFromRequest($this->_request, array_keys($typeMapping));
  450. } catch (Exeption $e) {
  451. //
  452. }
  453. if (empty($type) && isset($this->_request->callback)) {
  454. // JSONp
  455. $type = 'application/sparql-results+json';
  456. } else if (empty($type)) {
  457. // dafault: XML
  458. $type = 'application/sparql-results+xml';
  459. }
  460. try {
  461. // get result for mimetype
  462. $result = $store->sparqlQuery($query, array('result_format' => $typeMapping[$type]));
  463. } catch (Exception $e) {
  464. $response->setRawHeader('HTTP/1.1 400 Bad Request')
  465. ->setBody('MalformedQuery: ' . $e->getMessage())
  466. ->sendResponse();
  467. exit;
  468. }
  469. if (/* $typeMapping[$type] == 'json' && */isset($this->_request->callback)) {
  470. // return jsonp
  471. $response->setHeader('Content-Type', 'application/javascript');
  472. $padding = $this->_request->getParam('callback', '');
  473. $response->setBody($padding . '(' . $result . ')');
  474. } else {
  475. // set header
  476. $response->setHeader('Content-Type', $type);
  477. // return normally
  478. $response->setBody($result);
  479. }
  480. $response->sendResponse();
  481. exit;
  482. }
  483. }
  484. /**
  485. * OntoWiki Update Endpoint
  486. *
  487. * Only data inserts and deletes are implemented at the moment (e.g. no graph patterns).
  488. * @todo LOAD <> INTO <>, CLEAR GRAPH <>, CREATE[SILENT] GRAPH <>, DROP[ SILENT] GRAPH <>
  489. */
  490. public function updateAction()
  491. {
  492. // service controller needs no view renderer
  493. $this->_helper->viewRenderer->setNoRender();
  494. // disable layout for Ajax requests
  495. $this->_helper->layout()->disableLayout();
  496. $store = OntoWiki::getInstance()->erfurt->getStore();
  497. $response = $this->getResponse();
  498. $defaultGraph = $this->_request->getParam('default-graph-uri', null);
  499. $namedGraph = $this->_request->getParam('named-graph-uri', null);
  500. $insertGraph = null;
  501. $deleteGraph = null;
  502. $insertModel = null;
  503. $deleteModel = null;
  504. if (isset($this->_request->query)) {
  505. // we have a query, enter SPARQL/Update mode
  506. $query = $this->_request->getParam('query', '');
  507. OntoWiki::getInstance()->logger->info('SPARQL/Update query: ' . $query);
  508. $matches = array();
  509. // insert
  510. preg_match('/INSERT\s+DATA(\s+INTO\s*<(.+)>)?\s*{\s*([^}]*)/i', $query, $matches);
  511. $insertGraph = (isset($matches[2]) && ($matches[2] !== '')) ? $matches[2] : null;
  512. $insertTriples = isset($matches[3]) ? $matches[3] : '';
  513. if ((null === $insertGraph) && ($insertTriples !== '')) {
  514. if (null !== $defaultGraph) {
  515. $insertGraph = $defaultGraph;
  516. }
  517. if (null !== $namedGraph) {
  518. $insertGraph = $namedGraph;
  519. }
  520. }
  521. OntoWiki::getInstance()->logger->info('SPARQL/Update insertGraph: ' . $insertGraph);
  522. OntoWiki::getInstance()->logger->info('SPARQL/Update insertTriples: ' . $insertTriples);
  523. // delete
  524. preg_match('/DELETE\s+DATA(\s+FROM\s*<(.+)>)?\s*{\s*([^}]*)/i', $query, $matches);
  525. $deleteGraph = (isset($matches[2]) && ($matches[2] !== '')) ? $matches[2] : null;
  526. $deleteTriples = isset($matches[3]) ? $matches[3] : '';
  527. if ((null === $deleteGraph) && ($deleteTriples !== '')) {
  528. if (null !== $defaultGraph) {
  529. $deleteGraph = $defaultGraph;
  530. }
  531. if (null !== $namedGraph) {
  532. $deleteGraph = $namedGraph;
  533. }
  534. }
  535. // TODO: normalize literals
  536. $parser = Erfurt_Syntax_RdfParser::rdfParserWithFormat('nt');
  537. $insert = $parser->parse($insertTriples, Erfurt_Syntax_RdfParser::LOCATOR_DATASTRING);
  538. $parser->reset();
  539. $delete = $parser->parse($deleteTriples, Erfurt_Syntax_RdfParser::LOCATOR_DATASTRING);
  540. if (null !== $insertGraph) {
  541. try {
  542. $insertModel = $insertGraph ? $store->getModel($insertGraph) : $store->getModel($namedGraph);
  543. } catch (Erfurt_Store_Exception $e) {
  544. // TODO: error
  545. if (defined('_OWDEBUG')) {
  546. OntoWiki::getInstance()->logger->info('Could not instantiate models.');
  547. }
  548. exit;
  549. }
  550. }
  551. if (null !== $deleteGraph) {
  552. try {
  553. $deleteModel = $deleteGraph ? $store->getModel($deleteGraph) : $store->getModel($namedGraph);
  554. } catch (Erfurt_Store_Exception $e) {
  555. // TODO: error
  556. if (defined('_OWDEBUG')) {
  557. OntoWiki::getInstance()->logger->info('Could not instantiate models.');
  558. }
  559. exit;
  560. }
  561. }
  562. } else {
  563. // no query, inserts and delete triples by JSON via param
  564. $insert = json_decode($this->_request->getParam('insert', '{}'), true);
  565. $delete = json_decode($this->_request->getParam('delete', '{}'), true);
  566. if ($this->_request->has('delete_hashed')) {
  567. $hashedObjectStatements = $this->_findStatementsForObjectsWithHashes(
  568. $namedGraph,
  569. json_decode($this->_request->getParam('delete_hashed'), true));
  570. $delete = array_merge_recursive($delete, $hashedObjectStatements);
  571. }
  572. try {
  573. $namedModel = $store->getModel($namedGraph);
  574. $insertModel = $namedModel;
  575. $deleteModel = $namedModel;
  576. } catch (Erfurt_Store_Exception $e) {
  577. // TODO: error
  578. if (defined('_OWDEBUG')) {
  579. OntoWiki::getInstance()->logger->info('Could not instantiate models.');
  580. }
  581. exit;
  582. }
  583. }
  584. if (empty($insert) or empty($delete)) {
  585. // TODO: error
  586. }
  587. $flag = false;
  588. /**
  589. * @trigger onUpdateServiceAction is triggered when Service-Controller Update Action is executed.
  590. * Event contains following attributes:
  591. * deleteModel : model to delete statments from
  592. * deleteData : statements payload being deleted
  593. * insertModel : model to add statements to
  594. * insertDara : statements payload being added
  595. */
  596. $event = new Erfurt_Event('onUpdateServiceAction');
  597. $event->deleteModel = $deleteModel;
  598. $event->insertModel = $insertModel;
  599. $event->deleteData = $delete;
  600. $event->insertData = $insert;
  601. $event->trigger();
  602. // writeback
  603. $delete = $event->deleteData;
  604. $insert = $event->insertData;
  605. $changes = isset($event->changes) ? $event->changes : null;
  606. // delete
  607. if ($deleteModel && $deleteModel->isEditable()) {
  608. try {
  609. $count = $deleteModel->deleteMultipleStatements((array)$delete);
  610. } catch (Erfurt_Store_Exception $e) {
  611. if (defined('_OWDEBUG')) {
  612. OntoWiki::getInstance()->logger->info(
  613. 'Could not delete statements from graph: ' . $e->getMessage() . PHP_EOL .
  614. 'Statements: ' . print_r($delete, true)
  615. );
  616. }
  617. }
  618. $flag = true;
  619. if (defined('_OWDEBUG')) {
  620. OntoWiki::getInstance()->logger->info(
  621. sprintf('Deleted %i statements from graph <%s>', $count, $deleteModel->getModelUri())
  622. );
  623. }
  624. }
  625. // insert
  626. if ($insertModel && $insertModel->isEditable()) {
  627. $count = $insertModel->addMultipleStatements((array)$insert);
  628. $flag = true;
  629. if (defined('_OWDEBUG')) {
  630. OntoWiki::getInstance()->logger->info(
  631. sprintf('Inserted %i statements into graph <%s>', $count, $insertModel->getModelUri())
  632. );
  633. }
  634. }
  635. // nothing done?
  636. if (!$flag) {
  637. // When no user is given (Anoymous) give the requesting party a chance to authenticate.
  638. if (Erfurt_App::getInstance()->getAuth()->getIdentity()->isAnonymousUser()) {
  639. // In this case we allow the requesting party to authorize
  640. $response->setRawHeader('HTTP/1.1 401 Unauthorized');
  641. $response->setHeader('WWW-Authenticate', 'Basic realm="OntoWiki"');
  642. $response->sendResponse();
  643. exit;
  644. }
  645. }
  646. if ($changes) {
  647. /**
  648. * @see {http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.2.2}
  649. */
  650. $response->setHttpResponseCode(201);
  651. $response->setHeader('Location', $changes['changed']);
  652. $response->setHeader('Content-Type', 'application/json');
  653. $response->setBody(json_encode($changes));
  654. }
  655. }
  656. /**
  657. * Renders a template and responds with the output.
  658. *
  659. * All GET and POST parameters are populated into the view object
  660. * and therefore available in the view script. You have to know
  661. * which parameters the script uses and objects obviously cannot
  662. * be passed via GET/POST.
  663. */
  664. public function templateAction()
  665. {
  666. // fetch folder parameter
  667. if (isset($this->_request->f)) {
  668. $folder = $this->_request->getParam('f');
  669. } else {
  670. throw new OntoWiki_Exception('Missing parameter f!');
  671. exit;
  672. }
  673. // fetch template parameter
  674. if (isset($this->_request->t)) {
  675. $template = $this->_request->getParam('t');
  676. } else {
  677. throw new OntoWiki_Exception('Missing parameter t!');
  678. exit;
  679. }
  680. if (!preg_match('/^[a-z_]+$/', $folder) || !preg_match('/^[a-z_]+$/', $template)) {
  681. throw new OntoWiki_Exception('Illegal characters in folder or template name!');
  682. exit;
  683. }
  684. $path = _OWROOT . $this->_config->themes->path . $this->_config->themes->default . 'templates/' . $folder . DIRECTORY_SEPARATOR;
  685. $file = $template . '.' . $this->_helper->viewRenderer->getViewSuffix();
  686. if (!is_readable($path . $file)) {
  687. // $this->log('Template file not readable: ' . $path . $file, Zend_Log::ERR);
  688. throw new OntoWiki_Exception('Template file not readable. ' . $path . $file);
  689. exit;
  690. }
  691. // set script path
  692. $this->view->setScriptPath($path);
  693. // assign get and post parameters to view
  694. $this->view->assign($this->_request->getParams());
  695. // set header
  696. $this->_response->setRawHeader('Content-type: text/html');
  697. // render script
  698. $this->_response->setBody($this->view->render($file));
  699. }
  700. /**
  701. * JSON outputs of the transitive closure of resources to a given start
  702. * resource and an transitive attribute
  703. */
  704. public function transitiveclosureAction()
  705. {
  706. // service controller needs no view renderer
  707. $this->_helper->viewRenderer->setNoRender();
  708. // disable layout for Ajax requests
  709. $this->_helper->layout()->disableLayout();
  710. $store = OntoWiki::getInstance()->erfurt->getStore();
  711. $response = $this->getResponse();
  712. // fetch start resource parameter
  713. if (isset($this->_request->sr)) {
  714. $resource = $this->_request->getParam('sr', null, true);
  715. } else {
  716. throw new OntoWiki_Exception('Missing parameter sr (start resource)!');
  717. exit;
  718. }
  719. // fetch property resource parameter
  720. if (isset($this->_request->p)) {
  721. $property = $this->_request->getParam('p', null, true);
  722. } else {
  723. throw new OntoWiki_Exception('Missing parameter p (property)!');
  724. exit;
  725. }
  726. // m is automatically used and selected
  727. if ((!isset($this->_request->m)) && (!$this->_owApp->selectedModel)) {
  728. throw new OntoWiki_Exception('No model pre-selected model and missing parameter m (model)!');
  729. exit;
  730. } else {
  731. $model = $this->_owApp->selectedModel;
  732. }
  733. // fetch inverse parameter
  734. $inverse = $this->_request->getParam('inverse', 'true');
  735. switch ($inverse) {
  736. case 'false': /* fallthrough */
  737. case 'no': /* fallthrough */
  738. case 'off': /* fallthrough */
  739. case '0':
  740. $inverse = false;
  741. break;
  742. default:
  743. $inverse = true;
  744. }
  745. $store = $model->getStore();
  746. // get the transitive closure
  747. $closure = $store->getTransitiveClosure((string)$model, $property, array($resource), $inverse);
  748. // send the response
  749. $response->setHeader('Content-Type', 'application/json');
  750. $response->setBody(json_encode($closure));
  751. $response->sendResponse();
  752. exit;
  753. }
  754. /**
  755. * JSON output of the RDFauthor selection Cache File of the current model or
  756. * of the model given in parameter m
  757. */
  758. public function rdfauthorcacheAction()
  759. {
  760. // service controller needs no view renderer
  761. $this->_helper->viewRenderer->setNoRender();
  762. // disable layout for Ajax requests
  763. $this->_helper->layout()->disableLayout();
  764. $store = OntoWiki::getInstance()->erfurt->getStore();
  765. $response = $this->getResponse();
  766. $model = $this->_owApp->selectedModel;
  767. if (isset($this->_request->m)) {
  768. $model = $store->getModel($this->_request->m);
  769. }
  770. if (empty($model)) {
  771. throw new OntoWiki_Exception('Missing parameter m (model) and no selected model in session!');
  772. exit;
  773. }
  774. $output = array();
  775. $properties = $model->sparqlQuery('SELECT DISTINCT ?uri {
  776. ?uri a ?propertyClass.
  777. FILTER(
  778. sameTerm(?propertyClass, <'.EF_OWL_OBJECT_PROPERTY.'>) ||
  779. sameTerm(?propertyClass, <'.EF_OWL_DATATYPE_PROPERTY.'>) ||
  780. sameTerm(?propertyClass, <'.EF_OWL_ONTOLOGY_PROPERTY.'>) ||
  781. sameTerm(?propertyClass, <'.EF_RDF_PROPERTY.'>)
  782. )} LIMIT 200 ');
  783. if (!empty($properties)) {
  784. // push all URIs to titleHelper
  785. $titleHelper = new OntoWiki_Model_TitleHelper($model);
  786. foreach($properties as $property) {
  787. $titleHelper->addResource($property['uri']);
  788. }
  789. $lastProperty = end($properties);
  790. foreach($properties as $property) {
  791. $newProperty = array();
  792. // return title from titleHelper
  793. $newProperty['label'] = $titleHelper->getTitle($property['uri']);
  794. $pdata = $model->sparqlQuery('SELECT DISTINCT ?key ?value
  795. WHERE {
  796. <'.$property['uri'].'> ?key ?value
  797. FILTER(
  798. sameTerm(?key, <'.EF_RDF_TYPE.'>) ||
  799. sameTerm(?key, <'.EF_RDFS_DOMAIN.'>) ||
  800. sameTerm(?key, <'.EF_RDFS_RANGE.'>)
  801. )
  802. FILTER(isUri(?value))
  803. }
  804. LIMIT 20');
  805. if (!empty($pdata)) {
  806. $types = array();
  807. $ranges = array();
  808. $domains = array();
  809. // prepare the data in arrays
  810. foreach($pdata as $data) {
  811. if ( ($data['key'] == EF_RDF_TYPE) && ($data['value'] != EF_RDF_PROPERTY) ) {
  812. $types[] = $data['value'];
  813. } elseif ($data['key'] == EF_RDFS_RANGE) {
  814. $ranges[] = $data['value'];
  815. } elseif ($data['key'] == EF_RDFS_DOMAIN) {
  816. $domains[] = $data['value'];
  817. }
  818. }
  819. if (!empty($types)) {
  820. $newProperty['type'] = array_unique($types);
  821. }
  822. if (!empty($ranges)) {
  823. $newProperty['range'] = array_unique($ranges);
  824. }
  825. if (!empty($domains)) {
  826. $newProperty['domain'] = array_unique($domains);
  827. }
  828. }
  829. $output[ $property['uri'] ] = $newProperty;
  830. }
  831. }
  832. // send the response
  833. $response->setHeader('Content-Type', 'application/json');
  834. $response->setBody(json_encode($output));
  835. $response->sendResponse();
  836. exit;
  837. }
  838. /**
  839. * JSON output of the RDFauthor init config, which is a RDF/JSON Model
  840. * without objects where the user should be able to add data
  841. *
  842. * get/post parameters:
  843. * mode - class, resource or clone
  844. * class: prop list based on one class' resources
  845. * resource: prop list based on one resource
  846. * clone: prop list and values based on one resource (with new uri)
  847. * edit: prop list and values based on one resource
  848. * uri - parameter for mode (class uri, resource uri)
  849. */
  850. public function rdfauthorinitAction()
  851. {
  852. // service controller needs no view renderer
  853. $this->_helper->viewRenderer->setNoRender();
  854. // disable layout for Ajax requests
  855. $this->_helper->layout()->disableLayout();
  856. $store = OntoWiki::getInstance()->erfurt->getStore();
  857. $response = $this->getResponse();
  858. $model = $this->_owApp->selectedModel;
  859. if (isset($this->_request->m)) {
  860. $model = $store->getModel($this->_request->m);
  861. }
  862. if (empty($model)) {
  863. throw new OntoWiki_Exception('Missing parameter m (model) and no selected model in session!');
  864. exit;
  865. }
  866. if ( (isset($this->_request->uri)) && (Zend_Uri::check($this->_request->uri)) ) {
  867. $parameter = $this->_request->uri;
  868. } else {
  869. throw new OntoWiki_Exception('Missing or invalid parameter uri (clone uri) !');
  870. exit;
  871. }
  872. if (isset($this->_request->mode)) {
  873. $workingMode = $this->_request->mode;
  874. } else {
  875. $workingMode = 'resource';
  876. }
  877. if ($workingMode != 'edit') {
  878. $resourceUri = $model->getBaseUri(). 'newResource/' .md5(date('F j, Y, g:i:s:u a'));
  879. } else {
  880. $resourceUri = $parameter;
  881. }
  882. if ($workingMode == 'class') {
  883. $properties = $model->sparqlQuery('SELECT DISTINCT ?uri ?value {
  884. ?s ?uri ?value.
  885. ?s a <'.$parameter.'>.
  886. } LIMIT 20 ', array('result_format' => 'extended'));
  887. } elseif ($workingMode == 'clone') {
  888. # BUG: more than one values of a property are not supported right now
  889. # BUG: Literals are not supported right now
  890. $properties = $model->sparqlQuery('SELECT ?uri ?value {
  891. <'.$parameter.'> ?uri ?value.
  892. #FILTER (isUri(?value))
  893. } LIMIT 20 ', array('result_format' => 'extended'));
  894. } elseif ($workingMode == 'edit') {
  895. $properties = $model->sparqlQuery('SELECT ?uri ?value {
  896. <'.$parameter.'> ?uri ?value.
  897. } LIMIT 20 ', array('result_format' => 'extended'));
  898. } else { // resource
  899. $properties = $model->sparqlQuery('SELECT DISTINCT ?uri ?value {
  900. <'.$parameter.'> ?uri ?value.
  901. } LIMIT 20 ', array('result_format' => 'extended'));
  902. }
  903. // empty object to hold data
  904. $output = new stdClass();
  905. $newProperties = new stdClass();
  906. $properties = $properties['results']['bindings'];
  907. // feed title helper w/ URIs
  908. $titleHelper = new OntoWiki_Model_TitleHelper($model);
  909. $titleHelper->addResources($properties, 'uri');
  910. if (!empty($properties)) {
  911. foreach ($properties as $property) {
  912. $currentUri = $property['uri']['value'];
  913. $currentValue = $property['value']['value'];
  914. $currentType = $property['value']['type'];
  915. $value = new stdClass();
  916. if ($currentType == 'literal' || $currentType == 'typed-literal') {
  917. if (isset($property['value']['datatype'])) {
  918. $value->datatype = $property['value']['datatype'];
  919. } else if (isset($property['value']['xml:lang'])) {
  920. $value->lang = $property['value']['xml:lang'];
  921. }
  922. /* not in RDFauthor 0.8
  923. else {
  924. // plain literal --> rdfQuery needs extra quotes
  925. $currentValue = '"' . $currentValue . '"';
  926. }
  927. */
  928. }
  929. // return title from titleHelper
  930. $value->title = $titleHelper->getTitle($currentUri);
  931. if ($currentUri == EF_RDF_TYPE) {
  932. switch ($workingMode) {
  933. case 'resource':
  934. /* fallthrough */
  935. case 'clone':
  936. $value->value = $currentValue;
  937. break;
  938. case 'edit':
  939. $value->value = $currentValue;
  940. break;
  941. case 'class':
  942. $value->value = $parameter;
  943. break;
  944. }
  945. $value->type = $currentType;
  946. #$value->hidden = true;
  947. } else { // $currentUri != EF_RDF_TYPE
  948. if ( ($workingMode == 'clone') || ($workingMode == 'edit') ) {
  949. $value->value = $currentValue;
  950. $value->type = $currentType;
  951. }
  952. }
  953. // deal with multiple values of a property
  954. if (isset($newProperties->$currentUri)) {
  955. $tempProperty = $newProperties->$currentUri;
  956. $tempProperty[] = $value;
  957. $newProperties->$currentUri = $tempProperty;
  958. } else {
  959. $newProperties->$currentUri = array($value);
  960. }
  961. } // foreach
  962. $output->$resourceUri = $newProperties;
  963. } else {
  964. // empty sparql results -> start with a plain resource
  965. if ($workingMode == 'class') {
  966. // for classes, add the rdf:type property
  967. $value = new stdClass();
  968. $value->value = $parameter;
  969. $value->type = 'uri';
  970. $value->hidden = true;
  971. $uri = EF_RDF_TYPE;
  972. $newProperties->$uri = array($value);
  973. }
  974. $value = new stdClass();
  975. $value->type = 'literal';
  976. $value->title = 'label';
  977. $uri = EF_RDFS_LABEL;
  978. $newProperties->$uri = array($value);
  979. $output->$resourceUri = $newProperties;
  980. }
  981. // send the response
  982. $response->setHeader('Content-Type', 'application/json');
  983. $response->setBody(json_encode($output));
  984. $response->sendResponse();
  985. exit;
  986. }
  987. protected function _findStatementsForObjectsWithHashes($graphUri, $indexWithHashedObjects, $hashFunc = 'md5')
  988. {
  989. $queryOptions = array(
  990. 'result_format' => 'extended'
  991. );
  992. $result = array();
  993. foreach ($indexWithHashedObjects as $subject => $predicates) {
  994. foreach ($predicates as $predicate => $hashedObjects) {
  995. $query = "SELECT ?o FROM <$graphUri> WHERE {<$subject> <$predicate> ?o .}";
  996. $queryObj = Erfurt_Sparql_SimpleQuery::initWithString($query);
  997. if ($queryResult = $this->_owApp->erfurt->getStore()->sparqlQuery($queryObj, $queryOptions)) {
  998. $bindings = $queryResult['results']['bindings'];
  999. for ($i = 0, $max = count($bindings); $i < $max; $i++) {
  1000. $currentObject = $bindings[$i]['o'];
  1001. $objectString = Erfurt_Utils::buildLiteralString(
  1002. $currentObject['value'],
  1003. isset($currentObject['datatype']) ? $currentObject['datatype'] : null,
  1004. isset($currentObject['xml:lang']) ? $currentObject['xml:lang'] : null);
  1005. $hash = $hashFunc($objectString);
  1006. if (in_array($hash, $hashedObjects)) {
  1007. // add current statement to result
  1008. if (!isset($result[$subject])) {
  1009. $result[$subject] = array();
  1010. }
  1011. if (!isset($result[$subject][$predicate])) {
  1012. $result[$subject][$predicate] = array();
  1013. }
  1014. $objectSpec = array(
  1015. 'value' => $currentObject['value'],
  1016. 'type' => str_replace('typed-', '', $currentObject['type'])
  1017. );
  1018. if (isset($currentObject['datatype'])) {
  1019. $objectSpec['datatype'] = $currentObject['datatype'];
  1020. } else if (isset($currentObject['xml:lang'])) {
  1021. $objectSpec['lang'] = $currentObject['xml:lang'];
  1022. }
  1023. array_push($result[$subject][$predicate], $objectSpec);
  1024. }
  1025. }
  1026. }
  1027. }
  1028. }
  1029. return $result;
  1030. }
  1031. }