PageRenderTime 65ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/extensions/exconf/ExconfController.php

https://code.google.com/p/ontowiki/
PHP | 465 lines | 361 code | 52 blank | 52 comment | 88 complexity | dc9f292ea58ba069870f071af06f874b MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause
  1. <?php
  2. function checkRightsRec($dir) {
  3. $right = is_writable($dir);
  4. if ($right && is_dir($dir)) {
  5. $objects = scandir($dir);
  6. $curObjRight = false;
  7. foreach ($objects as $object) {
  8. if ($object != "." && $object != "..") {
  9. $curObjRight = checkRightsRec($dir."/".$object);
  10. if(!$curObjRight){
  11. $right = false;
  12. }
  13. }
  14. }
  15. }
  16. return $right;
  17. }
  18. function rrmdir($dir, $check = true) {
  19. if($check){
  20. if(!checkRightsRec($dir)){
  21. return false;
  22. }
  23. }
  24. if (is_dir($dir)) {
  25. $objects = scandir($dir);
  26. foreach ($objects as $object) {
  27. if ($object != "." && $object != "..") {
  28. if (filetype($dir."/".$object) == "dir") rrmdir($dir."/".$object, false); else unlink($dir."/".$object);
  29. }
  30. }
  31. reset($objects);
  32. rmdir($dir);
  33. }
  34. return true;
  35. }
  36. /**
  37. * edit extension configuration via a gui
  38. *
  39. * file permissions for the folders that contain a extension needs to allow modification
  40. * mostly that would be 0777
  41. *
  42. * @category OntoWiki
  43. * @package OntoWiki_extensions_components_exconf
  44. * @author Jonas Brekle <jonas.brekle@gmail.com>
  45. * @copyright Copyright (c) 2010, {@link http://aksw.org AKSW}
  46. * @license http://opensource.org/licenses/gpl-license.php GNU General Public License (GPL)
  47. */
  48. class ExconfController extends OntoWiki_Controller_Component {
  49. const EXTENSION_CLASS = "http://ns.ontowiki.net/Extensions/Extension";
  50. const EXTENSION_TITLE_PROPERTY = "http://www.w3.org/2000/01/rdf-schema#label"; //rdfs:label
  51. const EXTENSION_NAME_PROPERTY = "http://rdfs.org/sioc/ns#name"; //rdfs:label
  52. const EXTENSION_DESCRIPTION_PROPERTY = "http://purl.org/dc/elements/1.1/description"; //dc:description
  53. const EXTENSION_LATESTVERSION_PROPERTY = "http://ns.ontowiki.net/Extensions/latestVersion";
  54. const EXTENSION_LATESTRELEASELOCATION_PROPERTY = "http://ns.ontowiki.net/Extensions/latestReleaseLocation";
  55. protected $use_ftp = false;
  56. protected $writeable = true;
  57. protected $connection = null;
  58. protected $sftp = null;
  59. public function __call($method, $args) {
  60. echo "forward";
  61. $this->_forward('list');
  62. }
  63. public function init() {
  64. parent::init();
  65. OntoWiki_Navigation::reset();
  66. OntoWiki_Navigation::register('list', array('route' => null,
  67. 'controller' => 'exconf',
  68. 'action' => 'list',
  69. 'name' => 'List Installed'));
  70. OntoWiki_Navigation::register('repo', array('route' => null,
  71. 'controller' => 'exconf',
  72. 'action' => 'explorerepo',
  73. 'name' => 'Install from Repo'));
  74. OntoWiki_Navigation::register('upload', array('route' => null,
  75. 'controller' => 'exconf',
  76. 'action' => 'archiveuploadform',
  77. 'name' => 'Install from local upload'));
  78. $ow = OntoWiki::getInstance();
  79. $modMan = $ow->extensionManager;
  80. //determine how to write to the filesystem
  81. if(!is_writeable($modMan->getExtensionPath())){
  82. $con = $this->ftpConnect();
  83. if($con->connection == null){
  84. $this->writeable = false;
  85. $this->connection = false;
  86. $this->sftp = false;
  87. } else {
  88. $this->use_ftp = true;
  89. $this->connection = $con->connection;
  90. $this->sftp = $con->sftp;
  91. }
  92. }
  93. }
  94. function listAction() {
  95. $this->view->placeholder('main.window.title')->set($this->_owApp->translate->_('Configure Extensions'));
  96. $this->addModuleContext('main.window.exconf');
  97. $ow = OntoWiki::getInstance();
  98. if (!$this->_erfurt->getAc()->isActionAllowed('ExtensionConfiguration') && !$this->_request->isXmlHttpRequest()) {
  99. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("config not allowed for this user", OntoWiki_Message::ERROR));
  100. $this->view->isAllowed = false;
  101. $extensions = array();
  102. } else {
  103. $this->view->isAllowed = true;
  104. //get extension from manager
  105. $modMan = $ow->extensionManager;
  106. $extensions = $modMan->getExtensions();
  107. //sort by name property
  108. $volume = array();
  109. foreach ($extensions as $key => $row) {
  110. $volume[$key] = $row->name;
  111. }
  112. array_multisort($volume, SORT_ASC, $extensions);
  113. //some statistics
  114. $numEnabled = 0;
  115. $numDisabled = 0;
  116. foreach($extensions as $extension){
  117. if($extension->enabled){
  118. $numEnabled++;
  119. } else {
  120. $numDisabled++;
  121. }
  122. }
  123. $numAll = count($extensions);
  124. //save to view
  125. $this->view->numEnabled = $numEnabled;
  126. $this->view->numDisabled = $numDisabled;
  127. $this->view->numAll = $numAll;
  128. if(!is_writeable($modMan->getExtensionPath())){
  129. if(!$this->_request->isXmlHttpRequest()){
  130. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("the extension folder '".$modMan->getExtensionPath()."' is not writeable. no changes can be made", OntoWiki_Message::WARNING));
  131. }
  132. }
  133. }
  134. $this->view->extensions = $extensions;
  135. }
  136. function confAction(){
  137. OntoWiki_Navigation::disableNavigation();
  138. $this->view->placeholder('main.window.title')->set($this->_owApp->translate->_('Configure ').' '.$this->_request->getParam('name'));
  139. if (!$this->_erfurt->getAc()->isActionAllowed('ExtensionConfiguration')) {
  140. throw new OntoWiki_Exception("config not allowed for this user");
  141. } else {
  142. if(!isset($this->_request->name)){
  143. throw new OntoWiki_Exception("param 'name' needs to be passed to this action");
  144. }
  145. $ow = OntoWiki::getInstance();
  146. $toolbar = $ow->toolbar;
  147. $urlList = new OntoWiki_Url(array('controller'=>'exconf','action'=>'list'), array());
  148. $urlConf = new OntoWiki_Url(array('controller'=>'exconf','action'=>'conf'), array());
  149. $urlConf->restore = 1;
  150. $toolbar->appendButton(OntoWiki_Toolbar::SUBMIT, array('name' => 'save'))
  151. ->appendButton(OntoWiki_Toolbar::CANCEL, array('name' => 'back', 'class' => '', 'url' => (string) $urlList))
  152. ->appendButton(OntoWiki_Toolbar::EDIT, array('name' => 'restore defaults', 'class' => '', 'url' => (string) $urlConf));
  153. // add toolbar
  154. $this->view->placeholder('main.window.toolbar')->set($toolbar);
  155. $name = $this->_request->getParam('name');
  156. $manager = $ow->extensionManager;
  157. $dirPath = $manager->getExtensionPath(). $name .'/';
  158. if(!is_dir($dirPath)){
  159. throw new OntoWiki_Exception("invalid extension - does not exists");
  160. }
  161. $configFilePath = $dirPath.Ontowiki_Extension_Manager::DEFAULT_CONFIG_FILE;
  162. $localIniPath = $manager->getExtensionPath().$name.".ini";
  163. $privateConfig = $manager->getPrivateConfig($name);
  164. $config = ($privateConfig != null ? $privateConfig->toArray() : array());
  165. $this->view->enabled = $manager->isExtensionActive($name);
  166. $this->view->config = $config;
  167. $this->view->name = $name;
  168. if(!is_writeable($manager->getExtensionPath())){
  169. if(!$this->_request->isXmlHttpRequest()){
  170. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("the extension folder '".$manager->getExtensionPath()."' is not writeable. no changes can be made", OntoWiki_Message::WARNING));
  171. }
  172. } else {
  173. //react on post data
  174. if(isset($this->_request->remove)){
  175. if(rrmdir($dirPath)){
  176. $this->_redirect($this->urlBase.'exconf/list');
  177. } else {
  178. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("extension could not be deleted", OntoWiki_Message::ERROR));
  179. }
  180. }
  181. //the togglebuttons in the extension list action, send only a new enabled state
  182. if(isset($this->_request->enabled)){
  183. if(!file_exists($localIniPath)){
  184. @touch($localIniPath);
  185. chmod($localIniPath, 0777);
  186. }
  187. $ini = new Zend_Config_Ini($localIniPath, null, array('allowModifications' => true));
  188. $ini->enabled = $this->_request->getParam('enabled') == "true";
  189. $writer = new Zend_Config_Writer_Ini(array());
  190. $writer->write($localIniPath, $ini, true);
  191. }
  192. // the conf action sends a complete config array as json
  193. if(isset($this->_request->config)){
  194. $arr = json_decode($this->_request->getParam('config'), true);
  195. if($arr == null){
  196. throw new OntoWiki_Exception("invalid json: ".$this->_request->getParam('config'));
  197. } else {
  198. if(!file_exists($localIniPath)){
  199. @touch($localIniPath);
  200. chmod($localIniPath, 0777);
  201. }
  202. //only modification of the private section and the enabled-property are allowed
  203. foreach($arr as $key => $val){
  204. if($key != 'enabled' && $key != 'private'){
  205. unset($arr[$key]);
  206. }
  207. }
  208. $writer = new Zend_Config_Writer_Ini(array());
  209. $postIni = new Zend_Config($arr, true);
  210. $writer->write($localIniPath, $postIni, true);
  211. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("config sucessfully changed", OntoWiki_Message::SUCCESS));
  212. }
  213. $this->_redirect($this->urlBase.'exconf/conf/?name='.$name);
  214. }
  215. if(isset($this->_request->reset)){
  216. if(@unlink($localIniPath)){
  217. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("config sucessfully reverted to default", OntoWiki_Message::SUCCESS));
  218. } else {
  219. OntoWiki::getInstance()->appendMessage(new OntoWiki_Message("config not reverted to default - not existing or not writeable", OntoWiki_Message::ERROR));
  220. }
  221. $this->_redirect($this->urlBase.'exconf/conf/?name='.$name);
  222. }
  223. }
  224. }
  225. if($this->_request->isXmlHttpRequest()){
  226. //no rendering
  227. exit;
  228. }
  229. }
  230. public function explorerepoAction(){
  231. $this->view->placeholder('main.window.title')->set($this->_owApp->translate->_('Explore Repo'));
  232. $repoUrl = $this->_privateConfig->repoUrl;
  233. if(($otherRepo = $this->getParam("repoUrl")) != null){
  234. $repoUrl = $otherRepo;
  235. }
  236. $graph = $this->_privateConfig->graph;
  237. if(($otherGraph = $this->getParam("graph")) != null){
  238. $graph = $otherGraph;
  239. }
  240. $this->view->repoUrl = $repoUrl;
  241. $ow = OntoWiki::getInstance();
  242. $manager = $ow->extensionManager;
  243. $configs = $manager->getExtensions();
  244. $other = new stdClass();
  245. $other->configs = $configs;
  246. $listHelper = Zend_Controller_Action_HelperBroker::getStaticHelper('List');
  247. $listName = "extensions";
  248. if($listHelper->listExists($listName)){
  249. $list = $listHelper->getList($listName);
  250. $list->invalidate(); //remote repo may change data
  251. //$list->setStore($store); //TODO serialization replaces the store with the default store...
  252. $listHelper->addList($listName, $list, $this->view, "list_extensions_main", $other);
  253. } else {
  254. $adapter = new Erfurt_Store_Adapter_Sparql(array("serviceurl"=>$repoUrl, 'graphs'=>array($graph)));
  255. $store = new Erfurt_Store(array("adapterInstance"=>$adapter), "sparql");
  256. $rdfGraphObj = new Erfurt_Rdf_Model($graph);
  257. $list = new OntoWiki_Model_Instances($store, $rdfGraphObj, array());
  258. $list->addTypeFilter(self::EXTENSION_CLASS, null, array('withChilds'=>false));
  259. $list->addShownProperty(self::EXTENSION_NAME_PROPERTY, "name"); //internal name
  260. $list->addShownProperty(self::EXTENSION_TITLE_PROPERTY, "title"); //pretty name
  261. $list->addShownProperty(self::EXTENSION_DESCRIPTION_PROPERTY, "description");
  262. $list->addShownProperty(self::EXTENSION_LATESTVERSION_PROPERTY, "latestVersion");
  263. $list->addShownProperty(self::EXTENSION_LATESTRELEASELOCATION_PROPERTY, "latestReleaseLocation");
  264. $listHelper->addListPermanently($listName, $list, $this->view, "list_extensions_main", $other);
  265. }
  266. }
  267. /**
  268. * download a archive file from a remote webserver
  269. */
  270. public function installarchiveremoteAction(){
  271. $url = $this->getParam('url', "");
  272. if($url == ""){
  273. $ontoWiki->appendMessage(new OntoWiki_Message("parameter url needed.", OntoWiki_Message::ERROR));
  274. } else {
  275. $fileStr = file_get_contents($url);
  276. if($fileStr != false){
  277. $tmp = sys_get_temp_dir();
  278. if(!(substr($tmp, -1) == PATH_SEPARATOR)){
  279. $tmp .= PATH_SEPARATOR;
  280. }
  281. $tmpfname = tempnam($tmp, "OW_downloadedArchive.zip");
  282. $localFilehandle = fopen($tmpfname, "w+");
  283. fwrite($localFilehandle, $fileStr);
  284. rewind($localFilehandle);
  285. $this->installArchive($tmpfname);
  286. fclose($localFilehandle); //deletes file
  287. } else {
  288. $ontoWiki->appendMessage(new OntoWiki_Message("could not download.", OntoWiki_Message::ERROR));
  289. }
  290. }
  291. }
  292. /**
  293. * display a upload form
  294. */
  295. public function archiveuploadformAction(){
  296. $this->view->placeholder('main.window.title')->set('Upload new extension archive');
  297. $this->view->formActionUrl = $this->_config->urlBase . 'exconf/installarchiveupload';
  298. $this->view->formEncoding = 'multipart/form-data';
  299. $this->view->formClass = 'simple-input input-justify-left';
  300. $this->view->formMethod = 'post';
  301. $this->view->formName = 'archiveupload';
  302. $toolbar = $this->_owApp->toolbar;
  303. $toolbar->appendButton(OntoWiki_Toolbar::SUBMIT, array('name' => 'Upload Archive', 'id' => 'archiveupload'))
  304. ->appendButton(OntoWiki_Toolbar::RESET, array('name' => 'Cancel', 'id' => 'archiveupload'));
  305. $this->view->placeholder('main.window.toolbar')->set($toolbar);
  306. }
  307. /**
  308. * handle a archive upload (from browser)
  309. */
  310. public function installarchiveuploadAction(){
  311. if ($_FILES['archive_file']['error'] == UPLOAD_ERR_OK) {
  312. // upload ok, move file
  313. //$fileUri = $this->_request->getPost('file_uri');
  314. $fileName = $_FILES['archive_file']['name'];
  315. $tmpName = $_FILES['archive_file']['tmp_name'];
  316. $mimeType = $_FILES['archive_file']['type'];
  317. $cachedir = ini_get('upload_tmp_dir');
  318. $this->installArchive($cachedir.$tmpName);
  319. } else {echo "error";}
  320. }
  321. /**
  322. * handle a uploaded archive (from browser or remote webserver)
  323. * extract it to extension dir
  324. *
  325. * @param <type> $filePath
  326. * @param <type> $fileHandle
  327. */
  328. protected function installArchive($filePath){
  329. require_once 'pclzip.lib.php';
  330. $ext = mime_content_type($filePath);
  331. $this->view->success = false;
  332. $ontoWiki = OntoWiki::getInstance();
  333. switch ($ext){
  334. case "application/zip":
  335. $this->view->success = true;
  336. $zip = new PclZip($filePath);
  337. $modMan = $ontoWiki->extensionManager;
  338. $path = $modMan->getExtensionPath();
  339. //check the uploaded archive
  340. $content = $zip->listContent();
  341. $toplevelItem = null;
  342. $tooManyTopLevelItems = false;
  343. $sumBytes = 0;
  344. foreach($content as $key => $item){
  345. $level = substr_count($item["filename"], '/');
  346. if($level == 1 && substr($item["filename"],-1, 1) == "/"){
  347. if($toplevelItem === null){
  348. $toplevelItem = $key;
  349. } else {
  350. $tooManyTopLevelItems = true;
  351. break;
  352. }
  353. }
  354. $sumBytes += $item["size"];
  355. if($sumBytes >= 10000000){
  356. break;
  357. }
  358. }
  359. // extract contents of archive to disk (extension dir)
  360. if(!$tooManyTopLevelItems && $sumBytes < 10000000){ //only one item at top level allowed and max. 10MioB
  361. $extensionName = substr($content[$toplevelItem]['filename'], 0, -1);
  362. if(file_exists($path.$extensionName)){
  363. rrmdir($path.$extensionName);
  364. }
  365. $zip->extract(PCLZIP_OPT_PATH, $path);
  366. //make all writable so the files are not so alienated (otherwise, they can only be deleted by www-data or root)
  367. $iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($path.$extensionName),
  368. RecursiveIteratorIterator::CHILD_FIRST);
  369. foreach ($iterator as $name => $handle) {
  370. chmod($handle->__toString(), 0777);
  371. }
  372. $ontoWiki->appendMessage(new OntoWiki_Message($path.$extensionName, OntoWiki_Message::SUCCESS));
  373. $ontoWiki->appendMessage(new OntoWiki_Message("extension installed.", OntoWiki_Message::SUCCESS));
  374. } else {
  375. $ontoWiki->appendMessage(new OntoWiki_Message("uploaded archive was not accepted (must be < 10MB, and contain one folder).", OntoWiki_Message::ERROR));
  376. }
  377. break;
  378. default :
  379. $ontoWiki->appendMessage(new OntoWiki_Message("uploaded archive was not accepted (must be zip).", OntoWiki_Message::ERROR));
  380. break;
  381. }
  382. $url = new OntoWiki_Url(array('controller'=>'exconf', 'action'=>'explorerepo'));
  383. $this->_redirect($url);
  384. }
  385. protected function checkForUpdates(){
  386. }
  387. /**
  388. * Get the connection to ftp-server
  389. *
  390. * @param unknown_type $sftp
  391. * @param unknown_type $connection
  392. */
  393. public function ftpConnect(){
  394. if(isset($this->_privateConfig->ftp)){
  395. $username = $this->_privateConfig->ftp->username;
  396. $password = $this->_privateConfig->ftp->password;
  397. $hostname = $this->_privateConfig->ftp->hostname;
  398. //$ssh2 = "ssh2.sftp://$username:$password@$hostname:22";
  399. $connection = ssh2_connect($hostname, 22);
  400. ssh2_auth_password($connection, $username, $password);
  401. $sftp = ssh2_sftp($connection);
  402. $ret = new stdClass();
  403. $ret->connection = $connection;
  404. $ret->sftp = $sftp;
  405. return $ret;
  406. } else {
  407. $ret = new stdClass();
  408. $ret->connection = null;
  409. $ret->sftp = null;
  410. return $ret;
  411. }
  412. }
  413. }