ContentApiController.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523
  1. <?php
  2. namespace Typemill\Controllers;
  3. use Slim\Http\Request;
  4. use Slim\Http\Response;
  5. use Typemill\Models\Folder;
  6. use Typemill\Models\Write;
  7. use Typemill\Extensions\ParsedownExtension;
  8. class ContentApiController extends ContentController
  9. {
  10. public function publishArticle(Request $request, Response $response, $args)
  11. {
  12. # get params from call
  13. $this->params = $request->getParams();
  14. $this->uri = $request->getUri();
  15. # validate input
  16. if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
  17. # set structure
  18. if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
  19. # set item
  20. if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
  21. # set the status for published and drafted
  22. $this->setPublishStatus();
  23. # set path for the file (or folder)
  24. $this->setItemPath('md');
  25. # merge title with content for complete markdown document
  26. $updatedContent = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content'];
  27. # update the file
  28. if($this->write->writeFile($this->settings['contentFolder'], $this->path, $updatedContent))
  29. {
  30. # update the file
  31. $delete = $this->deleteContentFiles(['txt']);
  32. # update the internal structure
  33. $this->setStructure($draft = true, $cache = false);
  34. # update the public structure
  35. $this->setStructure($draft = false, $cache = false);
  36. return $response->withJson(['success'], 200);
  37. }
  38. else
  39. {
  40. return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
  41. }
  42. }
  43. public function unpublishArticle(Request $request, Response $response, $args)
  44. {
  45. # get params from call
  46. $this->params = $request->getParams();
  47. $this->uri = $request->getUri();
  48. # set structure
  49. if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
  50. # set item
  51. if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
  52. # set the status for published and drafted
  53. $this->setPublishStatus();
  54. # check if draft exists, if not, create one.
  55. if(!$this->item->drafted)
  56. {
  57. # set path for the file (or folder)
  58. $this->setItemPath('md');
  59. # set content of markdown-file
  60. if(!$this->setContent()){ return $response->withJson($this->errors, 404); }
  61. # initialize parsedown extension
  62. $parsedown = new ParsedownExtension();
  63. # turn markdown into an array of markdown-blocks
  64. $contentArray = $parsedown->markdownToArrayBlocks($this->content);
  65. # encode the content into json
  66. $contentJson = json_encode($contentArray);
  67. # set path for the file (or folder)
  68. $this->setItemPath('txt');
  69. /* update the file */
  70. if(!$this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
  71. {
  72. return $response->withJson(['errors' => ['message' => 'Could not create a draft of the page. Please check if the folder is writable']], 404);
  73. }
  74. }
  75. # update the file
  76. $delete = $this->deleteContentFiles(['md']);
  77. if($delete)
  78. {
  79. # update the internal structure
  80. $this->setStructure($draft = true, $cache = false);
  81. # update the live structure
  82. $this->setStructure($draft = false, $cache = false);
  83. return $response->withJson(['success'], 200);
  84. }
  85. else
  86. {
  87. return $response->withJson(['errors' => ['message' => "Could not delete some files. Please check if the files exists and are writable"]], 404);
  88. }
  89. }
  90. public function deleteArticle(Request $request, Response $response, $args)
  91. {
  92. # get params from call
  93. $this->params = $request->getParams();
  94. $this->uri = $request->getUri();
  95. # set url to base path initially
  96. $url = $this->uri->getBaseUrl() . '/tm/content';
  97. # set structure
  98. if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
  99. # set item
  100. if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
  101. if($this->item->elementType == 'file')
  102. {
  103. $delete = $this->deleteContentFiles(['md','txt']);
  104. }
  105. elseif($this->item->elementType == 'folder')
  106. {
  107. $delete = $this->deleteContentFolder();
  108. }
  109. if($delete)
  110. {
  111. # check if it is a subfile or subfolder and set the redirect-url to the parent item
  112. if(count($this->item->keyPathArray) > 1)
  113. {
  114. # get the parent item
  115. $parentItem = Folder::getParentItem($this->structure, $this->item->keyPathArray);
  116. if($parentItem)
  117. {
  118. # an active file has been moved to another folder
  119. $url .= $parentItem->urlRelWoF;
  120. }
  121. }
  122. # update the live structure
  123. $this->setStructure($draft = false, $cache = false);
  124. #update the backend structure
  125. $this->setStructure($draft = true, $cache = false);
  126. return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url), 200);
  127. }
  128. else
  129. {
  130. return $response->withJson(array('data' => $this->structure, 'errors' => $this->errors), 404);
  131. }
  132. }
  133. public function updateArticle(Request $request, Response $response, $args)
  134. {
  135. # get params from call
  136. $this->params = $request->getParams();
  137. $this->uri = $request->getUri();
  138. # validate input
  139. if(!$this->validateEditorInput()){ return $response->withJson($this->errors,422); }
  140. # set structure
  141. if(!$this->setStructure($draft = true)){ return $response->withJson($this->errors, 404); }
  142. # set item
  143. if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
  144. # set path for the file (or folder)
  145. $this->setItemPath('txt');
  146. # merge title with content for complete markdown document
  147. $updatedContent = '# ' . $this->params['title'] . "\r\n\r\n" . $this->params['content'];
  148. # initialize parsedown extension
  149. $parsedown = new ParsedownExtension();
  150. # turn markdown into an array of markdown-blocks
  151. $contentArray = $parsedown->markdownToArrayBlocks($updatedContent);
  152. # encode the content into json
  153. $contentJson = json_encode($contentArray);
  154. /* update the file */
  155. if($this->write->writeFile($this->settings['contentFolder'], $this->path, $contentJson))
  156. {
  157. # update the internal structure
  158. $this->setStructure($draft = true, $cache = false);
  159. return $response->withJson(['success'], 200);
  160. }
  161. else
  162. {
  163. return $response->withJson(['errors' => ['message' => 'Could not write to file. Please check if the file is writable']], 404);
  164. }
  165. }
  166. public function sortArticle(Request $request, Response $response, $args)
  167. {
  168. # get params from call
  169. $this->params = $request->getParams();
  170. $this->uri = $request->getUri();
  171. # url is only needed, if an active page is moved
  172. $url = false;
  173. # set structure
  174. if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
  175. # validate input
  176. if(!$this->validateNavigationSort()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Data not valid. Please refresh the page and try again.', 'url' => $url), 422); }
  177. # get the ids (key path) for item, old folder and new folder
  178. $itemKeyPath = explode('.', $this->params['item_id']);
  179. $parentKeyFrom = explode('.', $this->params['parent_id_from']);
  180. $parentKeyTo = explode('.', $this->params['parent_id_to']);
  181. # get the item from structure
  182. $item = Folder::getItemWithKeyPath($this->structure, $itemKeyPath);
  183. if(!$item){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
  184. # if a folder is moved on the first level
  185. if($this->params['parent_id_from'] == 'navi')
  186. {
  187. # create empty and default values so that the logic below still works
  188. $newFolder = new \stdClass();
  189. $newFolder->path = '';
  190. $folderContent = $this->structure;
  191. }
  192. else
  193. {
  194. # get the target folder from structure
  195. $newFolder = Folder::getItemWithKeyPath($this->structure, $parentKeyTo);
  196. # get the content of the target folder
  197. $folderContent = $newFolder->folderContent;
  198. }
  199. # if the item has been moved within the same folder
  200. if($this->params['parent_id_from'] == $this->params['parent_id_to'])
  201. {
  202. # get key of item
  203. $itemKey = end($itemKeyPath);
  204. reset($itemKeyPath);
  205. # delete item from folderContent
  206. unset($folderContent[$itemKey]);
  207. }
  208. elseif($this->params['active'] == 'active')
  209. {
  210. # an active file has been moved to another folder
  211. $url = $this->uri->getBaseUrl() . '/tm/content' . $newFolder->urlRelWoF . '/' . $item->slug;
  212. }
  213. # add item to newFolder
  214. array_splice($folderContent, $this->params['index_new'], 0, array($item));
  215. # initialize index
  216. $index = 0;
  217. # initialise write object
  218. $write = new Write();
  219. # iterate through the whole content of the new folder
  220. $writeError = false;
  221. foreach($folderContent as $folderItem)
  222. {
  223. if(!$write->moveElement($folderItem, $newFolder->path, $index))
  224. {
  225. $writeError = true;
  226. }
  227. $index++;
  228. }
  229. if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); }
  230. # update the structure for editor
  231. $this->setStructure($draft = true, $cache = false);
  232. # get item for url and set it active again
  233. if(isset($this->params['url']))
  234. {
  235. $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']);
  236. }
  237. # keep the internal structure for response
  238. $internalStructure = $this->structure;
  239. # update the structure for website
  240. $this->setStructure($draft = false, $cache = false);
  241. return $response->withJson(array('data' => $internalStructure, 'errors' => false, 'url' => $url));
  242. }
  243. public function createArticle(Request $request, Response $response, $args)
  244. {
  245. # get params from call
  246. $this->params = $request->getParams();
  247. $this->uri = $request->getUri();
  248. # url is only needed, if an active page is moved
  249. $url = false;
  250. # set structure
  251. if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
  252. # validate input
  253. if(!$this->validateNaviItem()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); }
  254. # get the ids (key path) for item, old folder and new folder
  255. $folderKeyPath = explode('.', $this->params['folder_id']);
  256. # get the item from structure
  257. $folder = Folder::getItemWithKeyPath($this->structure, $folderKeyPath);
  258. if(!$folder){ return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not find this page. Please refresh and try again.', 'url' => $url), 404); }
  259. # Rename all files within the folder to make sure, that namings and orders are correct
  260. # get the content of the target folder
  261. $folderContent = $folder->folderContent;
  262. # create the name for the new item
  263. $nameParts = Folder::getStringParts($this->params['item_name']);
  264. $name = implode("-", $nameParts);
  265. $slug = $name;
  266. # initialize index
  267. $index = 0;
  268. # initialise write object
  269. $write = new Write();
  270. # iterate through the whole content of the new folder
  271. $writeError = false;
  272. foreach($folderContent as $folderItem)
  273. {
  274. # check, if the same name as new item, then return an error
  275. if($folderItem->slug == $slug)
  276. {
  277. return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
  278. }
  279. if(!$write->moveElement($folderItem, $folder->path, $index))
  280. {
  281. $writeError = true;
  282. }
  283. $index++;
  284. }
  285. if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); }
  286. # add prefix number to the name
  287. $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name;
  288. $folderPath = 'content' . $folder->path;
  289. if($this->params['type'] == 'file')
  290. {
  291. if(!$write->writeFile($folderPath, $namePath . '.txt', ''))
  292. {
  293. return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the file. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
  294. }
  295. }
  296. elseif($this->params['type'] == 'folder')
  297. {
  298. if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
  299. {
  300. return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
  301. }
  302. $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', '');
  303. }
  304. # update the structure for editor
  305. $this->setStructure($draft = true, $cache = false);
  306. # get item for url and set it active again
  307. if(isset($this->params['url']))
  308. {
  309. $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']);
  310. }
  311. # activate this if you want to redirect after creating the page...
  312. # $url = $this->uri->getBaseUrl() . '/tm/content' . $folder->urlRelWoF . '/' . $name;
  313. return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url));
  314. }
  315. public function createBaseFolder(Request $request, Response $response, $args)
  316. {
  317. # get params from call
  318. $this->params = $request->getParams();
  319. $this->uri = $request->getUri();
  320. # url is only needed, if an active page is moved
  321. $url = false;
  322. # set structure
  323. if(!$this->setStructure($draft = true)){ return $response->withJson(array('data' => false, 'errors' => $this->errors, 'url' => $url), 404); }
  324. # validate input
  325. #if(!$this->validateBaseFolder()){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Special Characters not allowed. Length between 1 and 20 chars.', 'url' => $url), 422); }
  326. # create the name for the new item
  327. $nameParts = Folder::getStringParts($this->params['item_name']);
  328. $name = implode("-", $nameParts);
  329. $slug = $name;
  330. # initialize index
  331. $index = 0;
  332. # initialise write object
  333. $write = new Write();
  334. # iterate through the whole content of the new folder
  335. $writeError = false;
  336. foreach($this->structure as $folder)
  337. {
  338. # check, if the same name as new item, then return an error
  339. if($folder->slug == $slug)
  340. {
  341. return $response->withJson(array('data' => $this->structure, 'errors' => 'There is already a page with this name. Please choose another name.', 'url' => $url), 404);
  342. }
  343. if(!$write->moveElement($folder, '', $index))
  344. {
  345. $writeError = true;
  346. }
  347. $index++;
  348. }
  349. if($writeError){ return $response->withJson(array('data' => $this->structure, 'errors' => 'Something went wrong. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404); }
  350. # add prefix number to the name
  351. $namePath = $index > 9 ? $index . '-' . $name : '0' . $index . '-' . $name;
  352. $folderPath = 'content';
  353. if(!$write->checkPath($folderPath . DIRECTORY_SEPARATOR . $namePath))
  354. {
  355. return $response->withJson(array('data' => $this->structure, 'errors' => 'We could not create the folder. Please refresh the page and check, if all folders and files are writable.', 'url' => $url), 404);
  356. }
  357. $write->writeFile($folderPath . DIRECTORY_SEPARATOR . $namePath, 'index.txt', '');
  358. # update the structure for editor
  359. $this->setStructure($draft = true, $cache = false);
  360. # get item for url and set it active again
  361. if(isset($this->params['url']))
  362. {
  363. $activeItem = Folder::getItemForUrl($this->structure, $this->params['url']);
  364. }
  365. return $response->withJson(array('data' => $this->structure, 'errors' => false, 'url' => $url));
  366. }
  367. public function createBlock(Request $request, Response $response, $args)
  368. {
  369. /* get params from call */
  370. $this->params = $request->getParams();
  371. $this->uri = $request->getUri();
  372. /* validate input */
  373. if(!$this->validateInput()){ return $response->withJson($this->errors,422); }
  374. /* set structure */
  375. if(!$this->setStructure()){ return $response->withJson($this->errors, 404); }
  376. /* set item */
  377. if(!$this->setItem()){ return $response->withJson($this->errors, 404); }
  378. /* set path */
  379. $this->setItemPath();
  380. /* get markdown-file */
  381. if(!$this->setMarkdownFile()){ return $response->withJson($this->errors, 404); }
  382. /* get txt-file with content array */
  383. $contentArray = NULL;
  384. /*
  385. create a txt-file with parsedown-array.
  386. you will have .md and .txt file.
  387. scan folder with option to show drafts.
  388. but what is with structure? We use the cached structure, do not forget!!!
  389. if there is a draft, replace the md file with txt-file.
  390. display content: you have to check if md or txt. if txt, then directly open the txt-file.
  391. in here set markdown-file or
  392. set txt-file.
  393. if publish, render txt-content, replace markdown-file, delete txt-file
  394. */
  395. /* initialize pagedown */
  396. /* turn input into array */
  397. /* add input to contentArray */
  398. /* store updated contentArray */
  399. /* transform input to html */
  400. /* send html to client */
  401. }
  402. }