PageRenderTime 63ms CodeModel.GetById 29ms RepoModel.GetById 0ms app.codeStats 0ms

/modules/file.php

https://github.com/samphippen/srobo-ide
PHP | 481 lines | 326 code | 64 blank | 91 comment | 30 complexity | ad6f9429d0020aeb88ad36e20a4b217e MD5 | raw file
  1. <?php
  2. class FileModule extends Module
  3. {
  4. private $team;
  5. private $projectName;
  6. public function __construct()
  7. {
  8. $auth = AuthBackend::getInstance();
  9. // bail if we aren't authenticated
  10. if ($auth->getCurrentUser() == null)
  11. {
  12. // module does nothing if no authentication
  13. return;
  14. }
  15. $this->installCommand('compat-tree', array($this, 'getFileTreeCompat'));
  16. $this->installCommand('list', array($this, 'listFiles'));
  17. $this->installCommand('get', array($this, 'getFile'));
  18. $this->installCommand('put', array($this, 'putFile'));
  19. $this->installCommand('new', array($this, 'newFile'));
  20. $this->installCommand('del', array($this, 'deleteFile'));
  21. $this->installCommand('cp', array($this, 'copyFile'));
  22. $this->installCommand('mv', array($this, 'moveFile'));
  23. $this->installCommand('log', array($this, 'fileLog'));
  24. $this->installCommand('lint', array($this, 'lintFile'));
  25. $this->installCommand('diff', array($this, 'diff'));
  26. $this->installCommand('mkdir', array($this, 'makeDirectory'));
  27. $this->installCommand('co', array($this, 'checkoutFile'));
  28. }
  29. protected function initModule()
  30. {
  31. $this->projectManager = ProjectManager::getInstance();
  32. $input = Input::getInstance();
  33. $this->team = $input->getInput('team');
  34. // check that the project exists and is a git repo otherwise construct
  35. // the project directory and git init it
  36. $project = $input->getInput('project');
  37. $this->projectName = $project;
  38. }
  39. /**
  40. * Ensures that the user is in the team they claim to be
  41. */
  42. private function verifyTeam()
  43. {
  44. $auth = AuthBackend::getInstance();
  45. if (!in_array($this->team, $auth->getCurrentUserTeams()))
  46. {
  47. throw new Exception('proj attempted on team you aren\'t in', E_PERM_DENIED);
  48. }
  49. }
  50. /**
  51. * Gets a handle on the repository for the current project
  52. */
  53. private function repository()
  54. {
  55. $pm = ProjectManager::getInstance();
  56. $this->verifyTeam();
  57. $user = AuthBackend::getInstance()->getCurrentUser();
  58. $pm->updateRepository($this->team, $this->projectName, $user);
  59. $repo = $pm->getUserRepository($this->team, $this->projectName, $user);
  60. return $repo;
  61. }
  62. /**
  63. * Makes a directory in the repository
  64. */
  65. public function makeDirectory()
  66. {
  67. $input = Input::getInstance();
  68. $output = Output::getInstance();
  69. $path = $input->getInput("path");
  70. $success = $this->repository()->gitMKDir($path);
  71. $output->setOutput("success", $success ? 1 : 0);
  72. $text = $success ? 'Successfully created' : 'Failed to create';
  73. $output->setOutput("feedback", "$text folder '$path'");
  74. return $success;
  75. }
  76. /**
  77. * Gets a recursive file tree, optionally at a specific revision
  78. */
  79. public function getFileTreeCompat()
  80. {
  81. $input = Input::getInstance();
  82. $output = Output::getInstance();
  83. $revision = $input->getInput('rev', true);
  84. // a specific revision is requested
  85. if($revision != null && $revision != 'HEAD')
  86. {
  87. var_dump($this->projectName, $hash);
  88. $uncleanOut = $this->repository()->fileTreeCompat($this->projectName, '.', $revision);
  89. }
  90. else
  91. {
  92. $uncleanOut = $this->repository()->fileTreeCompat($this->projectName);
  93. }
  94. $results = $this->sanitiseFileList($uncleanOut);
  95. $output->setOutput('tree', $results);
  96. return true;
  97. }
  98. /**
  99. * Removes unwanted files from the given array.
  100. * Previously, this was used to hide __init__.py, but this file is now shown.
  101. */
  102. private function sanitiseFileList($unclean)
  103. {
  104. return array_values($unclean);
  105. }
  106. /**
  107. * Check out a particular revision of a file.
  108. * Also used to revert a file to its unmodified state.
  109. */
  110. public function checkoutFile()
  111. {
  112. $input = Input::getInstance();
  113. $output = Output::getInstance();
  114. $paths = $input->getInput("files");
  115. $revision = $input->getInput("revision");
  116. //latest
  117. $output->setOutput("rev", $revision);
  118. if ($revision === 0 || $revision === "HEAD")
  119. {
  120. foreach ($paths as $file)
  121. {
  122. $this->repository()->checkoutFile($file);
  123. }
  124. }
  125. else
  126. {
  127. $output->setOutput("revision reverting","");
  128. foreach ($paths as $file)
  129. {
  130. $this->repository()->checkoutFile($file,$revision);
  131. }
  132. }
  133. $output->setOutput("success",true);
  134. return true;
  135. }
  136. /**
  137. * Get a flat list of files in a specific folder
  138. */
  139. public function listFiles()
  140. {
  141. $input = Input::getInstance();
  142. $output = Output::getInstance();
  143. $path = $input->getInput('path');
  144. $uncleanOut = $this->repository()->listFiles($path);
  145. $results = $this->sanitiseFileList($uncleanOut);
  146. $output->setOutput('files', $results);
  147. return true;
  148. }
  149. /**
  150. * Get the contents of a given file in the repository
  151. */
  152. public function getFile()
  153. {
  154. $input = Input::getInstance();
  155. $output = Output::getInstance();
  156. $path = $input->getInput('path');
  157. $revision = $input->getInput('rev');
  158. // The data the repo has stored
  159. $original = $this->repository()->getFile($path, $revision);
  160. // only bother specifying the autosave data if HEAD
  161. $autosaved = null;
  162. if($revision == 'HEAD' && in_array($path, $this->repository()->unstagedChanges()))
  163. {
  164. $autosaved = $this->repository()->getFile($path);
  165. }
  166. $output->setOutput('autosaved', $autosaved);
  167. $output->setOutput('original', $original);
  168. return true;
  169. }
  170. /**
  171. * Save a file, without committing it
  172. */
  173. public function putFile()
  174. {
  175. $input = Input::getInstance();
  176. $path = $input->getInput('path');
  177. $data = $input->getInput('data');
  178. return $this->repository()->putFile($path, $data);
  179. }
  180. /**
  181. * Make a new file in the repository
  182. */
  183. public function newFile()
  184. {
  185. $input = Input::getInstance();
  186. $path = $input->getInput('path');
  187. return $this->repository()->createFile($path);
  188. }
  189. /**
  190. * Delete a given file in the repository
  191. */
  192. public function deleteFile()
  193. {
  194. $input = Input::getInstance();
  195. $output = Output::getInstance();
  196. $files = $input->getInput("files");
  197. foreach ($files as $file)
  198. {
  199. $this->repository()->removeFile($file);
  200. }
  201. return true;
  202. }
  203. /**
  204. * Copy a given file in the repository
  205. */
  206. public function copyFile()
  207. {
  208. $input = Input::getInstance();
  209. $output = Output::getInstance();
  210. $oldPath = $input->getInput('old-path');
  211. $newPath = $input->getInput('new-path');
  212. $this->repository()->copyFile($oldPath, $newPath);
  213. $output->setOutput('status', 0);
  214. $output->setOutput('message', $oldPath.' to '.$newPath);
  215. return true;
  216. }
  217. /**
  218. * Move a given file in the repository
  219. */
  220. public function moveFile()
  221. {
  222. $input = Input::getInstance();
  223. $output = Output::getInstance();
  224. $oldPath = $input->getInput('old-path');
  225. $newPath = $input->getInput('new-path');
  226. $this->repository()->moveFile($oldPath, $newPath);
  227. $output->setOutput('status', 0);
  228. $output->setOutput('message', $oldPath.' to '.$newPath);
  229. return true;
  230. }
  231. /**
  232. * Get the log for a file.
  233. * It expects a file to restrict the log to, and, optionally, an offset to start from and the number of entries wanted.
  234. * It returns the requested entries and a list of authors that have committed to the file.
  235. */
  236. public function fileLog()
  237. {
  238. $output = Output::getInstance();
  239. $input = Input::getInstance();
  240. $path = $input->getInput('path');
  241. $this->verifyTeam();
  242. $pm = ProjectManager::getInstance();
  243. $repo = $pm->getMasterRepository($this->team, $this->projectName);
  244. $number = $input->getInput('number', true);
  245. $offset = $input->getInput('offset', true);
  246. $number = ($number != null ? $number : 10);
  247. $offset = ($offset != null ? $offset * $number : 0);
  248. $log = $repo->log(null, null, $path);
  249. // if user has been passed we need to filter by author
  250. $user = $input->getInput("user", true);
  251. print $user;
  252. //take a backup of the log so we can list all the authors
  253. $originalLog = $log;
  254. //check if we've got a user and filter
  255. if ($user != null)
  256. {
  257. $filteredRevs = array();
  258. foreach ($log as $rev)
  259. {
  260. if ($rev["author"] == $user) $filteredRevs[] = $rev;
  261. }
  262. $log = $filteredRevs;
  263. }
  264. $output->setOutput('log', array_slice($log, $offset, $number));
  265. $output->setOutput('pages', ceil(count($log) / $number));
  266. $authors = array();
  267. foreach($originalLog as $rev)
  268. {
  269. $authors[] = $rev['author'];
  270. }
  271. $output->setOutput('authors', array_values(array_unique($authors)));
  272. return true;
  273. }
  274. /**
  275. * Gets the diff of:
  276. * A log change
  277. * The current state of a file against the tree
  278. */
  279. public function diff()
  280. {
  281. $output = Output::getInstance();
  282. $input = Input::getInstance();
  283. $hash = $input->getInput('hash');
  284. $path = $input->getInput('path');
  285. $newCode = $input->getInput('code', true);
  286. // patch from log
  287. if ($newCode === null)
  288. {
  289. $diff = $this->repository()->historyDiff($hash);
  290. }
  291. // diff against changed file
  292. else
  293. {
  294. $this->repository()->putFile($path, $newCode);
  295. $diff = $this->repository()->diff($path);
  296. }
  297. $output->setOutput("diff", $diff);
  298. return true;
  299. }
  300. /**
  301. * Checks a given file for errors
  302. */
  303. public function lintFile()
  304. {
  305. $input = Input::getInstance();
  306. $output = Output::getInstance();
  307. $config = Configuration::getInstance();
  308. $path = $input->getInput('path');
  309. //this occurs because someone decided it would be a good idea to split
  310. //these up here instead of javascript, makes this function hideous
  311. $splitPath = pathinfo($path);
  312. $dirName = $splitPath["dirname"];
  313. $fileName = $splitPath["filename"] . "." . $splitPath["extension"];
  314. // check for the reference file
  315. $dummy = $config->getConfig('pylint.referenceFile');
  316. if (!file_exists($dummy))
  317. {
  318. throw new Exception('Could not find dummy pyenv', E_NOT_IMPL);
  319. }
  320. //base dir might need changing with alistair's new git situation
  321. $base = $this->repository()->workingPath();
  322. //if the file exists, lint it otherwise return a dictionary explaining
  323. //that the file doesn't exist, shouldn't happen when users interface
  324. //with software because check syntax button always points at an existing file
  325. if (file_exists("$base/$path"))
  326. {
  327. $pylint = new PyLint();
  328. $importlint = new ImportLint();
  329. $useAutosave = $input->getInput('autosave', true);
  330. $revision = $input->getInput('rev', true);
  331. // Grab a temp folder that we can work in. We'll remove it later.
  332. $tmpDir = tmpdir();
  333. echo "base, path, tmp\n";
  334. var_dump($base, $path, $tmpDir);
  335. // Copy the user's files to the temp folder
  336. copy_recursive($base, $tmpDir);
  337. $working = $tmpDir.'/'.basename($base);
  338. // fixed revision
  339. if ($revision !== null)
  340. {
  341. // While this is in theory a file-level command, and we
  342. // therefore shouldn't be modding the whole repo, it doesn't
  343. // make any sense to just check out an old revision of just
  344. // the requested file.
  345. $repo = GitRepository::GetOrCreate($working);
  346. $repo->reset();
  347. $repo->checkoutRepo($revision);
  348. }
  349. // they want the current committed version of the file
  350. else if (!$useAutosave)
  351. {
  352. // TODO: do we also want to checkout the entire folder in this case?
  353. $repo = GitRepository::GetOrCreate($working);
  354. $repo->checkoutFile($path);
  355. $repo = null;
  356. }
  357. // Copy the reference file to the tenp folder
  358. $dummy_copy = $working.'/'.basename($dummy);
  359. echo "dummy copy\n";
  360. var_dump($dummy_copy);
  361. copy($dummy, $dummy_copy);
  362. $errors = array();
  363. $importErrors = $importlint->lintFile($working, $path);
  364. if ($importErrors === False)
  365. {
  366. $pyErrors = $pylint->lintFile($working, $path);
  367. if ($pyErrors !== False)
  368. {
  369. $errors = $pyErrors;
  370. }
  371. else
  372. {
  373. // remove the temporary folder
  374. delete_recursive($tmpDir);
  375. // Both sets of linting failed, so fail overall.
  376. return False;
  377. }
  378. }
  379. else
  380. {
  381. $errors = $importErrors;
  382. $more_files = $importlint->getTouchedFiles();
  383. $pyErrors = $pylint->lintFiles($working, $more_files);
  384. if ($pyErrors !== False)
  385. {
  386. $errors = array_merge($errors, $pyErrors);
  387. }
  388. else
  389. {
  390. // remove the temporary folder
  391. delete_recursive($tmpDir);
  392. // Both sets of linting failed, so fail overall.
  393. return False;
  394. }
  395. }
  396. // remove the temporary folder
  397. delete_recursive($tmpDir);
  398. // Sort & convert to jsonables if needed.
  399. // This (latter) step necessary currently since JSONSerializeable doesn't exist yet.
  400. if (count($errors) > 0)
  401. {
  402. usort($errors, function($a, $b) {
  403. if ($a->lineNumber == $b->lineNumber) return 0;
  404. return $a->lineNumber > $b->lineNumber ? 1 : -1;
  405. });
  406. $errors = array_map(function($lm) { return $lm->toJSONable(); }, $errors);
  407. }
  408. $output->setOutput("errors", $errors);
  409. return true;
  410. }
  411. else
  412. {
  413. $output->setOutput('error', 'file does not exist');
  414. return false;
  415. }
  416. }
  417. }