BxWikiStudioPage.php 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361
  1. <?php defined('BX_DOL') or die('hack attempt');
  2. /**
  3. * Copyright (c) UNA, Inc - https://una.io
  4. * MIT License - https://opensource.org/licenses/MIT
  5. *
  6. * @defgroup Wiki Wiki
  7. * @ingroup UnaModules
  8. *
  9. * @{
  10. */
  11. class BxWikiStudioPage extends BxTemplStudioModule
  12. {
  13. function __construct($sModule, $mixedPageName, $sPage = "")
  14. {
  15. parent::__construct($sModule, $mixedPageName, $sPage);
  16. $this->aMenuItems = array(
  17. 'settings' => ['name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'],
  18. 'import' => ['name' => 'import', 'icon' => 'download', 'title' => '_bx_wiki_import'],
  19. );
  20. }
  21. function getImport ()
  22. {
  23. $aForm = array(
  24. 'form_attrs' => array(
  25. 'name' => 'bx-wiki-import',
  26. 'method' => 'post',
  27. ),
  28. 'params' => array(
  29. 'db' => array(
  30. 'submit_name' => 'do_submit',
  31. ),
  32. ),
  33. 'inputs' => array(
  34. 'repo' => array(
  35. 'type' => 'text',
  36. 'name' => 'repo',
  37. 'caption' => _t('_bx_wiki_repository_url'),
  38. 'required' => true,
  39. ),
  40. 'base_url' => array(
  41. 'type' => 'text',
  42. 'name' => 'base_url',
  43. 'caption' => _t('_bx_wiki_base_url'),
  44. 'required' => true,
  45. ),
  46. 'lang' => array(
  47. 'type' => 'select',
  48. 'name' => 'lang',
  49. 'caption' => _t('_bx_wiki_lang'),
  50. 'values' => BxDolLanguages::getInstance()->getLanguages(),
  51. 'value' => BxDolLanguages::getInstance()->getCurrentLanguage(),
  52. ),
  53. 'unsafe' => array(
  54. 'type' => 'checkbox',
  55. 'name' => 'unsafe',
  56. 'caption' => _t('_bx_wiki_unsafe'),
  57. ),
  58. 'add_titles' => array(
  59. 'type' => 'checkbox',
  60. 'name' => 'add_titles',
  61. 'caption' => _t('_bx_wiki_add_titles'),
  62. ),
  63. 'git' => array(
  64. 'type' => 'text',
  65. 'name' => 'git',
  66. 'caption' => _t('_bx_wiki_repository_git'),
  67. 'required' => true,
  68. 'value' => 'git',
  69. ),
  70. 'ext' => array(
  71. 'type' => 'text',
  72. 'name' => 'ext',
  73. 'caption' => _t('_bx_wiki_ext'),
  74. 'required' => true,
  75. 'value' => 'md',
  76. ),
  77. 'skip_files' => array(
  78. 'type' => 'textarea',
  79. 'name' => 'skip_files',
  80. 'caption' => _t('_bx_wiki_skip_files'),
  81. 'value' => '["_Footer.md"]',
  82. ),
  83. 'only_files' => array(
  84. 'type' => 'textarea',
  85. 'name' => 'only_files',
  86. 'caption' => _t('_bx_wiki_only_files'),
  87. ),
  88. 'page_uri_map' => array(
  89. 'type' => 'textarea',
  90. 'name' => 'page_uri_map',
  91. 'caption' => _t('_bx_wiki_page_uri_map'),
  92. 'value' => '{"Home": "Index", "Dashboard": "Wiki-Dashboard"}',
  93. ),
  94. 'submit' => array(
  95. 'type' => 'submit',
  96. 'name' => 'do_submit', // the same as key and database field name
  97. 'value' => _t('_bx_wiki_import'),
  98. ),
  99. ),
  100. );
  101. $oForm = new BxTemplFormView($aForm);
  102. $oForm->initChecker();
  103. if ($oForm->isSubmittedAndValid ()) {
  104. $sGit =
  105. $sRepoUrl =
  106. $aParams = [
  107. 'git' => $oForm->getCleanValue('git'),
  108. 'repository' => $oForm->getCleanValue('repo'),
  109. 'base_url' => $oForm->getCleanValue('base_url'),
  110. 'lang' => $oForm->getCleanValue('lang'),
  111. 'unsafe' => (bool)$oForm->getCleanValue('unsafe'),
  112. 'add_titles' => (bool)$oForm->getCleanValue('add_titles'),
  113. 'ext' => $oForm->getCleanValue('ext'),
  114. 'skip_files' => json_decode($oForm->getCleanValue('skip_files'), true),
  115. 'only_files' => json_decode($oForm->getCleanValue('only_files'), true),
  116. 'page_uri_map' => json_decode($oForm->getCleanValue('page_uri_map'), true),
  117. ];
  118. $sErrorMsg = '';
  119. $b = $this->import($aParams, $sErrorMsg);
  120. self::removeTmpFolder();
  121. if ($b)
  122. return _t('_bx_wiki_import_success');
  123. else
  124. return $sErrorMsg;
  125. }
  126. else {
  127. return $oForm->getCode();
  128. }
  129. }
  130. static public function import($aParams, &$sErrorMsg)
  131. {
  132. $sGit = $aParams['git'];
  133. $sRepoUrl = $aParams['repository'];
  134. $sExt = $aParams['ext'];
  135. $aSkipFiles = $aParams['skip_files'];
  136. $aOnlyFiles = $aParams['only_files'];
  137. $sErrorMsg = '';
  138. // empty directory to clone repository to
  139. $sDir = BX_DIRECTORY_PATH_TMP . 'bx_wiki_import';
  140. if (!self::removeTmpFolder()) {
  141. $sErrorMsg = "$sDir can't be removed";
  142. return false;
  143. }
  144. // clone repository
  145. $sCmd = "{$sGit} clone {$sRepoUrl} " . $sDir;
  146. $aOutput = null;
  147. $iRetVal = null;
  148. if (false === exec(escapeshellcmd($sCmd), $aOutput, $iRetVal) || (null !== $iRetVal && $iRetVal)) {
  149. $sErrorMsg = "Command($sCmd) execution failed: <pre>" . implode("\n", $aOutput) . "</pre>";
  150. return false;
  151. }
  152. if (false === ($h = opendir($sDir))) {
  153. $sErrorMsg = "Directory($sDir) read failed";
  154. return false;
  155. }
  156. // get list of files to import and check pages for existence
  157. $aFilesToImport = [];
  158. while (false !== ($sFile = readdir($h))) {
  159. if (!empty($aSkipFiles) && in_array($sFile, $aSkipFiles))
  160. continue;
  161. if (!empty($aOnlyFiles) && !in_array($sFile, $aOnlyFiles))
  162. continue;
  163. $a = pathinfo($sDir . '/' . $sFile);
  164. if (empty($a['filename']) || empty($a['extension']) || $a['extension'] != $sExt)
  165. continue;
  166. $sErrorMsgFile = '';
  167. if (self::isPageExist($aParams, $sDir . '/' . $sFile, $a['filename'], $sErrorMsgFile)) {
  168. $sErrorMsg = "File($sFile) import failed: $sErrorMsgFile";
  169. break;
  170. }
  171. $aFilesToImport[$sFile] = $a;
  172. }
  173. closedir($h);
  174. if ($sErrorMsg)
  175. return false;
  176. // perform import
  177. foreach ($aFilesToImport as $sFile => $a) {
  178. $sErrorMsgFile = '';
  179. if (!self::importFile($aParams, $sDir . '/' . $sFile, $a['filename'], $sErrorMsgFile)) {
  180. $sErrorMsg = "File($sFile) import failed: $sErrorMsgFile";
  181. break;
  182. }
  183. }
  184. if ($sErrorMsg)
  185. return false;
  186. return true;
  187. }
  188. static protected function isPageExist($aParams, $sPath, $sTitle, &$sErrorMsg)
  189. {
  190. $aMapPageUris = $aParams['page_uri_map'];
  191. $sUri = $sTitle;
  192. if (isset($aMapPageUris[$sUri]))
  193. $sUri = $aMapPageUris[$sUri];
  194. if ($oPage = BxDolPage::getObjectInstanceByURI($sUri)) {
  195. $sErrorMsg = "Page with such URI($sUri) already exists";
  196. return true;
  197. }
  198. return false;
  199. }
  200. static protected function importFile($aParams, $sPath, $sTitle, &$sErrorMsg)
  201. {
  202. $sBaseUrlOld = $aParams['base_url'];
  203. $sLang = $aParams['lang'];
  204. $bUnsafe = $aParams['unsafe'];
  205. $bAddTitles = $aParams['add_titles'];
  206. $aMapPageUris = $aParams['page_uri_map'];
  207. if (false === ($sContents = file_get_contents($sPath)) || empty($sContents)) {
  208. $sErrorMsg = "File($sPath) read failed";
  209. return false;
  210. }
  211. $oWiki = BxDolWiki::getObjectInstance('bx_wiki');
  212. $sUri = $sTitle;
  213. if (isset($aMapPageUris[$sUri]))
  214. $sUri = $aMapPageUris[$sUri];
  215. $sTitle = str_replace('-', ' ', $sTitle);
  216. if (!($iPageId = $oWiki->addPage($sUri, $sTitle))) {
  217. $sErrorMsg = "Page with URI($sUri) add failed";
  218. return false;
  219. }
  220. if (!($oPage = BxDolPage::getObjectInstanceByURI($sUri))) {
  221. $sErrorMsg = "Page with URI($sUri) wasn't found";
  222. return false;
  223. }
  224. $iDesignBox = getParam('bx_wiki_design_box');
  225. $aBlock = array(
  226. 'object' => $oPage->getName(),
  227. 'cell_id' => 1,
  228. 'module' => 'bx_wiki',
  229. 'title_system' => '',
  230. 'title' => '',
  231. 'designbox_id' => false === $iDesignBox ? 0 : $iDesignBox,
  232. 'visible_for_levels' => 2147483647,
  233. 'type' => 'wiki',
  234. 'deletable' => 1,
  235. 'copyable' => 0,
  236. 'active' => 1,
  237. );
  238. $oQueryPageBuilder = new BxDolStudioBuilderPageQuery();
  239. if (!($iBlockId = $oQueryPageBuilder->insertBlock($aBlock))) {
  240. $sErrorMsg = "Page(" . $oPage->getName() . ") block add failed";
  241. return false;
  242. }
  243. $sContents = self::processImages($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId);
  244. $sContents = self::processContents($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId);
  245. if ($bAddTitles)
  246. $sContents = "# $sTitle\n\n" . $sContents;
  247. $oForm = BxDolForm::getObjectInstance('sys_wiki', 'sys_wiki_edit');
  248. $sIdRevision = $oForm->insert(array(
  249. 'block_id' => $iBlockId,
  250. 'revision' => 1,
  251. 'language' => $sLang,
  252. 'main_lang' => 1,
  253. 'profile_id' => 0,
  254. 'content' => $sContents,
  255. 'unsafe' => $bUnsafe,
  256. 'notes' => 'Import',
  257. 'added' => time(),
  258. ));
  259. if (!$sIdRevision) {
  260. $sErrorMsg = "Revision add failed for block ($iBlockId)";
  261. return false;
  262. }
  263. $oWiki->updateBlockIndexingData($iBlockId);
  264. return true;
  265. }
  266. static protected function processContents($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId)
  267. {
  268. // strip base URL
  269. $sContents = preg_replace("/\(" . preg_quote($sBaseUrlOld, '/') . "([^\)]+)\)/", "[$1]", $sContents);
  270. // replace special constructions which don't work locally
  271. $sContents = preg_replace('/\[\[(.+?)\|(.+?)\]\]/', '[$1][$2]', $sContents);
  272. $sContents = preg_replace('/\[(.+?)\|(.+?)\]/', '[$1][$2]', $sContents);
  273. // replace old URIs with new ones
  274. foreach ($aMapPageUris as $sOldUri => $sNewUri)
  275. $sContents = str_replace("[$sOldUri]", "[$sNewUri]", $sContents);
  276. // convert links to the proper ones
  277. $sContents = preg_replace('/\[(.+?)\]\[(http:\/\/.+?)\]/', '[$1]($2)', $sContents);
  278. $sContents = preg_replace('/\[(.+?)\]\[(https:\/\/.+?)\]/', '[$1]($2)', $sContents);
  279. return $sContents;
  280. }
  281. static protected function processImages($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId)
  282. {
  283. // unify links to images
  284. $sContents = preg_replace('/\[\[(.+?)\|alt=(.+?)\]\]/', '![$2]($1)', $sContents);
  285. // process images
  286. $sContents = preg_replace_callback('/\!\[(.+?)\]\((.+?)\)/', function ($aMatches) use ($sBaseUrlOld, $iBlockId) {
  287. if (empty($aMatches[2]))
  288. return $aMatches[0];
  289. // strip base Wiki URL
  290. $sImg = str_replace($sBaseUrlOld, "", $aMatches[2]);
  291. // skip external URLs
  292. if (0 === strncmp($sImg, 'http://', 7) || 0 === strncmp($sImg, 'https://', 8))
  293. return $aMatches[0];
  294. $sImgPath = BX_DIRECTORY_PATH_TMP . 'bx_wiki_import/' . ltrim($sImg, '/');
  295. if (!file_exists($sImgPath))
  296. return $aMatches[0];
  297. $oStorage = BxDolStorage::getObjectInstance('sys_wiki_files');
  298. if (!$oStorage)
  299. return $aMatches[0];
  300. if (!($iFileId = $oStorage->storeFileFromPath($sImgPath, false, 0, $iBlockId)))
  301. return $aMatches[0];
  302. if (!($aFile = $oStorage->getFile($iFileId)))
  303. return $aMatches[0];
  304. return "![{$aMatches[2]}](" . $oStorage->getObject() . "/" . $aFile['remote_id'] . ")";
  305. }, $sContents);
  306. return $sContents;
  307. }
  308. static public function removeTmpFolder()
  309. {
  310. $sDir = BX_DIRECTORY_PATH_TMP . 'bx_wiki_import';
  311. return !file_exists($sDir) || bx_rrmdir($sDir);
  312. }
  313. }
  314. /** @} */