BxDolWiki.php 43 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085
  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 UnaCore UNA Core
  7. * @{
  8. */
  9. /**
  10. * WIKI object.
  11. *
  12. * It's possble to create different WIKI object which will different URLs, Menus and permissions.
  13. * For example it's possible to create
  14. * http://example.com/wiki/somepageshere and http://example.com/docs/anotherpageshere
  15. *
  16. * @section wiki_create Creating the WIKI object:
  17. *
  18. *
  19. * Add record to 'sys_objects_wiki' table:
  20. *
  21. * - object: name of the WIKI object, this name will be user in URL as well,
  22. * for example, 'wiki' will have URLs like this: http://example.com/wiki/somepageshere
  23. * - uri: wiki module URI
  24. * - title: title of the WIKI, for example, documentation, help, tutorial
  25. * - module: module name WIKI object belongs to
  26. * - override_class_name: user defined class name
  27. * - override_class_file: the location of the user defined class, leave it empty if class is located in system folders.
  28. *
  29. * Add record to 'sys_rewrite_rules' table:
  30. * - preg - regular expression which matches some URL
  31. * - service - service method to call if abbe regular expression matches
  32. * - active - active flag
  33. *
  34. * Add record to 'sys_permalinks' table:
  35. * - standard - how link should look like when permalinks are off
  36. * - permalink - how link should look like when permalinks are on,
  37. * some special server configuration may be required to make permalink to work,
  38. * such as 'mod_rewrite' and .htaccess file for Apache
  39. * - check - option name which enables/disables permalinks
  40. * - compare_by_prefix - compare by prefix
  41. */
  42. class BxDolWiki extends BxDolFactory implements iBxDolFactoryObject
  43. {
  44. protected $_bIsApi;
  45. protected $_sObject;
  46. protected $_aObject;
  47. protected $_oQuery;
  48. protected $_sLangCateg = 'Wiki';
  49. protected $_sACLprefix = 'wiki ';
  50. protected $_bProcessMarkdown = true;
  51. /**
  52. * Constructor
  53. * @param $aObject array of WIKI options
  54. */
  55. protected function __construct($aObject)
  56. {
  57. parent::__construct();
  58. $this->_bIsApi = bx_is_api();
  59. $this->_sObject = $aObject['object'];
  60. $this->_aObject = $aObject;
  61. $this->_oQuery = new BxDolWikiQuery($aObject);
  62. }
  63. /**
  64. * Get WIKI object instance by object URI
  65. * @param $sObject object name
  66. * @return object instance or false on error
  67. */
  68. static public function getObjectInstanceByUri($sUri, $oTemplate = false)
  69. {
  70. $aObject = BxDolWikiQuery::getWikiObjectByUri($sUri);
  71. if (!$aObject || !is_array($aObject))
  72. return false;
  73. return self::getObjectInstance($aObject['object'], $oTemplate);
  74. }
  75. /**
  76. * Get WIKI object instance by object name
  77. * @param $sObject object name
  78. * @return object instance or false on error
  79. */
  80. static public function getObjectInstance($sObject, $oTemplate = false)
  81. {
  82. if (isset($GLOBALS['bxDolClasses']['BxDolWiki!'.$sObject]))
  83. return $GLOBALS['bxDolClasses']['BxDolWiki!'.$sObject];
  84. $aObject = BxDolWikiQuery::getWikiObject($sObject);
  85. if (!$aObject || !is_array($aObject))
  86. $aObject = BxDolWikiQuery::getWikiObject('system');
  87. $sClass = empty($aObject['override_class_name']) ? 'BxDolWiki' : $aObject['override_class_name'];
  88. if (!empty($aObject['override_class_file']))
  89. require_once(BX_DIRECTORY_PATH_ROOT . $aObject['override_class_file']);
  90. $o = new $sClass($aObject, $oTemplate);
  91. return ($GLOBALS['bxDolClasses']['BxDolWiki!'.$sObject] = $o);
  92. }
  93. static public function onModuleUninstall($sModule)
  94. {
  95. $aBlockIds = BxDolWikiQuery::getBlocks ($sModule);
  96. if ($aBlockIds && is_array($aBlockIds))
  97. self::onBlockDelete($aBlockIds);
  98. }
  99. static public function onBlockDelete($mixedBlockIds)
  100. {
  101. BxDolWikiQuery::deleteAllRevisions($mixedBlockIds);
  102. $aBlockIds = is_array($mixedBlockIds) ? $mixedBlockIds : [$mixedBlockIds];
  103. $oStorage = BxDolStorage::getObjectInstance('sys_wiki_files');
  104. if ($oStorage) {
  105. foreach ($aBlockIds as $iBlockId) {
  106. $aFiles = $oStorage->getGhosts(0, $iBlockId, false, true);
  107. $oStorage->queueFiles($aFiles);
  108. }
  109. }
  110. }
  111. /**
  112. * Get object name
  113. */
  114. public function getObjectName ()
  115. {
  116. return $this->_sObject;
  117. }
  118. /**
  119. * Get URI
  120. */
  121. public function getUri ()
  122. {
  123. return $this->_aObject['uri'];
  124. }
  125. /**
  126. * Get module name
  127. */
  128. public function getModule ()
  129. {
  130. return $this->_aObject['module'];
  131. }
  132. /**
  133. * Get WIKI block content
  134. * @param $iBlockId block ID
  135. * @param $sLang optional language name
  136. * @return block content
  137. */
  138. public function getBlockContent ($iBlockId, $sLang = false, $iRevision = false)
  139. {
  140. if (!$sLang)
  141. $sLang = bx_lang_name();
  142. $sContent = '';
  143. $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sLang, $iRevision);
  144. $aWikiLatest = $this->_oQuery->getBlockContent ($iBlockId, $sLang);
  145. if ($aWikiVer) {
  146. if ($this->_bProcessMarkdown && !$this->_bIsApi) {
  147. $oParsedown = new BxDolParsedown();
  148. $oParsedown->setSafeMode($aWikiVer['unsafe'] ? false : true);
  149. $sContent = $oParsedown->text($aWikiVer['content']);
  150. }
  151. else
  152. $sContent = $aWikiVer['content'];
  153. }
  154. if (!$aWikiVer && $aWikiLatest) {
  155. return ($sMsg = _t('_sys_wiki_error_no_rev', $iRevision ? $iRevision : 0, $sLang)) && $this->_bIsApi ? ['content' => $sMsg] : $sMsg;
  156. }
  157. if (!$aWikiVer && !$aWikiLatest && $this->isAllowed('edit')) {
  158. $sContent = _t('_sys_wiki_error_no_revs');
  159. }
  160. $mixedControls = '';
  161. if ($aWikiVer || $this->isAllowed('edit'))
  162. $mixedControls = bx_srv('system', 'wiki_controls', [$this, $aWikiVer, $aWikiLatest, $iBlockId], 'TemplServiceWiki');
  163. return $this->_bIsApi ? array_merge([
  164. 'content' => $sContent,
  165. 'menu' => ''
  166. ], $mixedControls) : $sContent . $mixedControls;
  167. }
  168. /**
  169. * Generate wiki contents - list of all wiki pages
  170. */
  171. public function getContents ($sAllExceptSpecified = '', $sOnlySpecified = '')
  172. {
  173. $aAllExceptSpecified = $sAllExceptSpecified ? explode(',', $sAllExceptSpecified) : array();
  174. $aAllExceptSpecified = array_merge($aAllExceptSpecified, ['wiki-pages-list', 'wiki-page-contents']);
  175. $aOnlySpecified = $sOnlySpecified ? explode(',', $sOnlySpecified) : array();
  176. if (!($a = $this->_oQuery->getPages($aAllExceptSpecified, $aOnlySpecified)))
  177. return '';
  178. $aVars = array('bx_repeat:pages' => array());
  179. foreach ($a as $r) {
  180. $oPage = BxDolPage::getObjectInstance($r['object']);
  181. if (!$oPage->isVisiblePage())
  182. continue;
  183. $sUrl = $this->getPageUrl($r['uri']);
  184. $sTitle = _t($r['title']);
  185. $aVars['bx_repeat:pages'][] = array(
  186. 'url' => $sUrl,
  187. 'title' => $sTitle ? $sTitle : _t('_Empty'),
  188. );
  189. }
  190. usort($aVars['bx_repeat:pages'], function ($r1, $r2) {
  191. return strcmp($r1['title'], $r2['title']);
  192. });
  193. return BxDolTemplate::getInstance()->parseHtmlByName('wiki_contents.html', $aVars);
  194. }
  195. /**
  196. * Generate list of wiki blocks with missing translations for the given language
  197. */
  198. public function getMissingTranslations ($sLang = false)
  199. {
  200. if (!$sLang)
  201. $sLang = bx_lang_name();
  202. if (!$this->isAllowed('edit') && !$this->isAllowed('translate') || !preg_match("/^[a-z]+$/", $sLang))
  203. return _t('_sys_txt_access_denied');
  204. if (!BxDolLanguages::getInstance()->getLangId($sLang))
  205. return _t('_sys_wiki_error_no_such_lang');
  206. $a = $this->_oQuery->getBlocksWithMissingTranslations($sLang);
  207. if (!$a)
  208. return _t('_sys_wiki_no_missing_translations', $sLang);
  209. $aVars = array(
  210. 'title_title' => _t('_sys_wiki_title_title'),
  211. 'title_lang' => _t('_sys_wiki_title_lang_missing'),
  212. 'title_author' => _t('_sys_wiki_title_author', _t('_sys_wiki_orig')),
  213. 'title_content' => _t('_sys_wiki_title_content', _t('_sys_wiki_orig')),
  214. 'title_time' => _t('_sys_wiki_title_time', _t('_sys_wiki_orig')),
  215. 'bx_repeat:blocks' => array()
  216. );
  217. foreach ($a as $iBlockId) {
  218. $aWikiVerMain = $this->_oQuery->getBlockContent ($iBlockId, false);
  219. $aPage = $this->_oQuery->getPageByBlockId($iBlockId);
  220. $sUrl = $this->getPageUrl($aPage['uri']) . '#bx-page-wiki-container-' . $iBlockId;
  221. $oProfile = BxDolProfile::getInstanceMagic($aWikiVerMain['profile_id']);
  222. $aVars['bx_repeat:blocks'][] = array(
  223. 'author_url' => $oProfile->getUrl(),
  224. 'author_name' => $oProfile->getDisplayName(),
  225. 'url' => $sUrl,
  226. 'lang' => $sLang,
  227. 'title' => _t($aPage['title']),
  228. 'content' => strmaxtextlen($aWikiVerMain['content']),
  229. 'timejs' => bx_time_js($aWikiVerMain['added']),
  230. );
  231. }
  232. return BxDolTemplate::getInstance()->parseHtmlByName('wiki_translations_missing.html', $aVars);
  233. }
  234. /**
  235. * Generate list of wiki blocks with outdated translations for the given language
  236. */
  237. public function getOutdatedTranslations ($sLang = false)
  238. {
  239. if (!$sLang)
  240. $sLang = bx_lang_name();
  241. if (!$this->isAllowed('edit') && !$this->isAllowed('translate') || !preg_match("/^[a-z]+$/", $sLang))
  242. return _t('_sys_txt_access_denied');
  243. if (!BxDolLanguages::getInstance()->getLangId($sLang))
  244. return _t('_sys_wiki_error_no_such_lang');
  245. $aVars = array(
  246. 'title_title' => _t('_sys_wiki_title_title'),
  247. 'title_lang' => _t('_sys_wiki_title_lang_outated'),
  248. 'title_author' => _t('_sys_wiki_title_author', _t('_sys_wiki_orig')),
  249. 'title_content' => _t('_sys_wiki_title_content', _t('_sys_wiki_orig')),
  250. 'title_time_main' => _t('_sys_wiki_title_time', _t('_sys_wiki_orig')),
  251. 'title_time' => _t('_sys_wiki_title_time', '"'.$sLang.'"'),
  252. 'bx_repeat:blocks' => array()
  253. );
  254. $a = $this->_oQuery->getBlocksWithOutdatedTranslations($sLang);
  255. if (!$a)
  256. return _t('_sys_wiki_all_translations_up_to_date', $sLang);
  257. foreach ($a as $iBlockId) {
  258. $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sLang, false, false);
  259. $aWikiVerMain = $this->_oQuery->getBlockContent ($iBlockId, false);
  260. $aPage = $this->_oQuery->getPageByBlockId($iBlockId);
  261. $sUrl = $this->getPageUrl($aPage['uri']) . '#bx-page-wiki-container-' . $iBlockId;
  262. $oProfile = BxDolProfile::getInstanceMagic($aWikiVerMain['profile_id']);
  263. $aVars['bx_repeat:blocks'][] = array(
  264. 'author_url' => $oProfile->getUrl(),
  265. 'author_name' => $oProfile->getDisplayName(),
  266. 'url' => $sUrl,
  267. 'lang' => $sLang,
  268. 'title' => _t($aPage['title']),
  269. 'content' => strmaxtextlen($aWikiVerMain['content']),
  270. 'timejs_main' => bx_time_js($aWikiVerMain['added']),
  271. 'timejs' => bx_time_js($aWikiVer['added']),
  272. );
  273. }
  274. return BxDolTemplate::getInstance()->parseHtmlByName('wiki_translations_outdated.html', $aVars);
  275. }
  276. /**
  277. * Check if partucular action is allowed
  278. * @param $sType action type: add, edit, delete, translate
  279. * @param $sProfileId profile to check, if not provided then current profile is used
  280. * @return true if action is allowed, false otherwise
  281. */
  282. public function isAllowed ($sType, $iProfileId = false)
  283. {
  284. if ('convert-links' == $sType) {
  285. return isLogged();
  286. }
  287. // translate isn't allowed when only one language on the site
  288. if ('translate' == $sType) {
  289. $aLangs = BxDolLanguages::getInstance()->getLanguages(false, true);
  290. if (count($aLangs) < 2)
  291. return false;
  292. }
  293. // add page isn't implemented for the system
  294. if ('add-page' == $sType && 'system' == $this->_aObject['module'])
  295. return false;
  296. // any action is allowed for admin(operator)
  297. if (isAdmin())
  298. return true;
  299. // map 'action' to 'acl action'
  300. $aTypes = array(
  301. 'add-page' => 'add page',
  302. 'add' => 'add block',
  303. 'edit' => 'edit block',
  304. 'translate' => 'translate block',
  305. 'delete-version' => 'delete version',
  306. 'delete-block' => 'delete block',
  307. 'get-traaslation' => 'translate block',
  308. 'history' => 'history',
  309. 'unsafe' => 'unsafe',
  310. );
  311. // not listed actions aren't allowed
  312. if (!isset($aTypes[$sType]))
  313. return false;
  314. // hardcoded actions (not implemented for now)
  315. if (true === $aTypes[$sType] || false === $aTypes[$sType])
  316. return $aTypes[$sType];
  317. // all system actions starts with special prefix
  318. $sAction = ('system' == $this->_aObject['module'] ? $this->_sACLprefix : '') . $aTypes[$sType];
  319. if (!$iProfileId)
  320. $iProfileId = bx_get_logged_profile_id();
  321. $aCheck = checkActionModule($iProfileId, $sAction, $this->_aObject['module']);
  322. return $aCheck[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED;
  323. }
  324. public function addPage ($sPageUri, $sTitleLangKey)
  325. {
  326. $sUrl = $this->getPageUrl($sPageUri, false, false);
  327. return $this->_oQuery->insertPage ($sPageUri, $sUrl, $sTitleLangKey);
  328. }
  329. public function actionGetTranslation ()
  330. {
  331. $aWikiVer = $this->_oQuery->getBlockContent ((int)bx_get('block_id'), bx_get('language'), false, false);
  332. if (!$aWikiVer)
  333. return array('code' => 1, 'actions' => 'ShowMsg', 'msg' => 'no translation was found');
  334. else
  335. return array('code' => 0, 'language' => $aWikiVer['language'], 'content' => $aWikiVer['content'], 'block_id' => $aWikiVer['block_id']);
  336. }
  337. public function actionDeleteVersion ($iBlockId = 0, $mixedParams = false)
  338. {
  339. if(!$iBlockId && ($_iBlockId = bx_get('block_id')) !== false)
  340. $iBlockId = (int)$_iBlockId;
  341. $sLang = bx_lang_name();
  342. $oLang = BxDolLanguages::getInstance();
  343. $aVars = $this->getVarsForHistory($iBlockId, $sLang);
  344. $aVars['select_all'] = _t('_Select_all');
  345. if (!$aVars['bx_repeat:revisions'])
  346. return ($sMsg = _t('_sys_wiki_error_no_revs')) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : BxDolTemplate::getInstance()->parseHtmlByName('wiki_msg.html', array(
  347. 'close' => _t('_sys_close'),
  348. 'msg' => $sMsg,
  349. ));
  350. if($this->_bIsApi) {
  351. if($mixedParams && is_string($mixedParams)) {
  352. $i = $this->_oQuery->deleteRevisions ($iBlockId, $sLang, explode(',', $mixedParams));
  353. return [bx_api_get_msg(_t('_sys_wiki_revisions_deleted', $i), ['ext' => ['code' => 0, 'block_id' => $iBlockId]])];
  354. }
  355. return [bx_api_get_block('wiki_history', $aVars['bx_repeat:revisions'], ['ext' => [
  356. 'mode' => 'edit',
  357. 'request_url' => 'system/wiki_action/TemplServiceWiki&params[]=' . $this->getUri() . '&params[]=delete-version&params[]=' . $iBlockId . '&params[]=',
  358. ]])];
  359. }
  360. $aForm = array(
  361. 'form_attrs' => array(
  362. 'name' => 'bx-wiki-del-rev',
  363. 'method' => 'post',
  364. ),
  365. 'params' => array (
  366. 'db' => array(
  367. 'submit_name' => 'do_submit',
  368. ),
  369. ),
  370. 'inputs' => array(
  371. 'block_id' => array(
  372. 'type' => 'hidden',
  373. 'name' => 'block_id',
  374. 'value' => $iBlockId,
  375. ),
  376. 'language' => array(
  377. 'type' => 'hidden',
  378. 'name' => 'language',
  379. 'value' => $sLang,
  380. ),
  381. 'revision' => array(
  382. 'type' => 'custom',
  383. 'name' => 'revision',
  384. 'caption' => '',
  385. 'content' => BxDolTemplate::getInstance()->parseHtmlByName('wiki_delete_version.html', $aVars),
  386. ),
  387. 'buttons' => array(
  388. 'type' => 'input_set',
  389. array(
  390. 'type' => 'submit',
  391. 'name' => 'do_submit',
  392. 'value' => _t('_Submit'),
  393. ),
  394. array(
  395. 'type' => 'reset',
  396. 'name' => 'close',
  397. 'value' => _t('_sys_close'),
  398. 'attrs' => array(
  399. 'onclick' => "$('.bx-popup-applied:visible').dolPopupHide();",
  400. 'class' => "bx-def-margin-sec-left",
  401. ),
  402. ),
  403. ),
  404. ),
  405. );
  406. $oForm = new BxTemplFormView ($aForm);
  407. $oForm->initChecker();
  408. if ($oForm->isSubmittedAndValid ()) {
  409. $i = $this->_oQuery->deleteRevisions ($iBlockId, $sLang, bx_get('revision'));
  410. return ['code' => 0, 'actions' => ['Reload', 'ClosePopup', 'ShowMsg'], 'block_id' => $iBlockId, 'msg' => _t('_sys_wiki_revisions_deleted', $i)];
  411. }
  412. else {
  413. return BxDolTemplate::getInstance()->parseHtmlByName('wiki_form.html', array(
  414. 'form' => $oForm->getCode(),
  415. 'block_id' => $iBlockId,
  416. 'wiki_action_uri' => $this->getUri(),
  417. 'action' => 'delete-version',
  418. 'txt_open_editor' => bx_js_string(_t('_sys_wiki_open_in_editor')),
  419. ));
  420. }
  421. }
  422. public function actionAddPage ($sPageUri = '')
  423. {
  424. if(!$sPageUri && ($_sPage = bx_get('page')) !== false)
  425. $sPageUri = bx_process_input($_sPage);
  426. // validate page URI
  427. if(!preg_match("/^[a-z0-9_-]+$/", $sPageUri))
  428. return ($sMsg = _t('_sys_wiki_error_incorrect_page_uri', bx_process_output($sPageUri))) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : $sMsg;
  429. // check if such page exist
  430. $oPage1 = BxDolPage::getObjectInstanceByUri($sPageUri);
  431. $oPage2 = BxDolPage::getObjectInstance($this->_aObject['module'] . '_' . str_replace('-', '_', $sPageUri));
  432. if($oPage1 || $oPage2)
  433. return ($sMsg = _t('_sys_wiki_error_page_exists', bx_process_output($sPageUri))) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : $sMsg;
  434. // get lang category where translations will be added
  435. $oLang = BxDolStudioLanguagesUtils::getInstance();
  436. $iLangCat = $oLang->getLanguageCategory($this->_sLangCateg);
  437. if(!$iLangCat)
  438. return ($sMsg = _t('_sys_wiki_error_occured', 9)) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : $sMsg;
  439. // set default values for the form
  440. $aValues = array();
  441. $aLangs = $oLang->getLanguages(false, true);
  442. foreach ($aLangs as $sKey => $sLang)
  443. $aValues[$sKey] = ucfirst($sPageUri);
  444. // init form
  445. $aForm = array(
  446. 'form_attrs' => array(
  447. 'name' => 'bx-wiki-create-page',
  448. 'method' => 'post',
  449. ),
  450. 'params' => array (
  451. 'db' => array(
  452. 'submit_name' => 'do_submit',
  453. ),
  454. ),
  455. 'inputs' => array(
  456. 'page' => array(
  457. 'type' => 'hidden',
  458. 'name' => 'page',
  459. 'value' => $sPageUri,
  460. ),
  461. 'title' => array(
  462. 'type' => 'text_translatable',
  463. 'name' => 'title',
  464. 'caption' => _t('_adm_bp_txt_page_title'),
  465. 'info' => _t('_adm_bp_dsc_page_title'),
  466. 'value' => '',
  467. 'values' => $aValues,
  468. 'required' => '1',
  469. 'db' => array (
  470. 'pass' => 'Xss',
  471. ),
  472. 'checker' => array (
  473. 'func' => 'LengthTranslatable',
  474. 'params' => array(3,100, 'title'),
  475. 'error' => _t('_adm_bp_err_page_title'),
  476. ),
  477. ),
  478. 'buttons' => array(
  479. 'type' => 'input_set',
  480. array(
  481. 'type' => 'submit',
  482. 'name' => 'do_submit',
  483. 'value' => _t('_Submit'),
  484. ),
  485. array(
  486. 'type' => 'reset',
  487. 'name' => 'close',
  488. 'value' => _t('_sys_close'),
  489. 'attrs' => array(
  490. 'onclick' => "$('.bx-popup-applied:visible').dolPopupHide();",
  491. 'class' => "bx-def-margin-sec-left",
  492. ),
  493. ),
  494. ),
  495. ),
  496. );
  497. $oForm = new BxTemplStudioFormView ($aForm);
  498. $oForm->initChecker();
  499. if ($oForm->isSubmittedAndValid()) {
  500. // insert page
  501. $sLangKey = '_' . $this->_aObject['module'] . '_' . str_replace('-', '_', $sPageUri) . '_' . time();
  502. $iPageId = $this->addPage ($sPageUri, $sLangKey);
  503. if (!$iPageId)
  504. return ($sMsg = _t('_sys_wiki_error_occured', 10)) && $this->_bIsApi ? [bx_api_get_msg($sMsg, ['ext' => ['code' => 10]])] : ['code' => 10, 'actions' => ['ShowMsg'], 'msg' => $sMsg];
  505. // insert translations
  506. $aLangs = $oLang->getLanguages(true, true);
  507. foreach ($aLangs as $iLangId => $sLangTitle) {
  508. $sLang = $oLang->getLangName($iLangId);
  509. if(!($sVal = $oForm->getCleanValue('title-' . $sLang)))
  510. continue;
  511. $oLang->addLanguageString($sLangKey, $sVal, $iLangId, $iLangCat);
  512. }
  513. $sUrl = $this->getPageUrl($sPageUri, false, false);
  514. return ['code' => 0, 'url' => BxDolPermalinks::getInstance()->permalink($sUrl)];
  515. }
  516. else {
  517. // display form
  518. return $this->_bIsApi ? [bx_api_get_block('form', $oForm->getCodeAPI(), [
  519. 'ext' => [
  520. 'name' => $this->_sObject,
  521. 'request' => ['url' => '/api.php?r=system/wiki_action/TemplServiceWiki&params[]=' . $this->getUri() . '&params[]=add-page&params[]=' . $sPageUri, 'immutable' => true]
  522. ]
  523. ])] : BxDolTemplate::getInstance()->parseHtmlByName('wiki_create_page_form.html', [
  524. 'form' => $oForm->getCode(),
  525. 'wiki_action_uri' => $this->getUri(),
  526. 'action' => 'add-page',
  527. ]);
  528. }
  529. }
  530. public function actionAdd($sPage = '', $iCellId = 0)
  531. {
  532. if(!$sPage && ($_sPage = bx_get('page')) !== false)
  533. $sPage = bx_process_input($_sPage);
  534. if(!$iCellId && ($_iCellId = bx_get('cell_id')) !== false)
  535. $iCellId = (int)$_iCellId;
  536. $oPage = BxDolPage::getObjectInstance($sPage);
  537. if(!$oPage)
  538. return ($sMsg = _t('_sys_wiki_error_occured', 4)) && $this->_bIsApi ? [bx_api_get_msg($sMsg, ['ext' => ['code' => 4]])] : ['code' => 4, 'actions' => ['ShowMsg'], 'msg' => $sMsg];
  539. if($this->_aObject['module'] != $oPage->getModule())
  540. return ($sMsg = _t('_sys_txt_access_denied')) && $this->_bIsApi ? [bx_api_get_msg($sMsg, ['ext' => ['code' => 5]])] : ['code' => 5, 'actions' => ['ShowMsg'], 'msg' => $sMsg];
  541. $oQueryPageBuilder = new BxDolStudioBuilderPageQuery();
  542. if(!($iBlockId = $oQueryPageBuilder->insertBlock([
  543. 'object' => $sPage,
  544. 'cell_id' => $iCellId,
  545. 'module' => $this->_aObject['module'],
  546. 'title_system' => '',
  547. 'title' => '',
  548. 'designbox_id' => (int)getParam($this->_aObject['module'] . '_design_box'),
  549. 'visible_for_levels' => 2147483647,
  550. 'type' => 'wiki',
  551. 'deletable' => 1,
  552. 'copyable' => 0,
  553. 'active' => 1,
  554. 'active_api' => 1,
  555. ])))
  556. return ($sMsg = _t('_sys_wiki_error_occured', 6)) && $this->_bIsApi ? [bx_api_get_msg($sMsg, ['ext' => ['code' => 6]])] : ['code' => 6, 'actions' => ['ShowMsg'], 'msg' => $sMsg];
  557. return ['code' => 0, 'block_id' => $iBlockId, 'cell_id' => $iCellId];
  558. }
  559. public function actionDeleteBlock ($iBlockId = 0)
  560. {
  561. if(!$iBlockId && ($_iBlockId = bx_get('block_id')) !== false)
  562. $iBlockId = (int)$_iBlockId;
  563. $oQueryPageBuilder = new BxDolStudioBuilderPageQuery();
  564. if (!$oQueryPageBuilder->deleteBlocks(['type' => 'by_id', 'value' => $iBlockId]))
  565. return ($sMsg = _t('_sys_wiki_error_occured', 3)) && $this->_bIsApi ? [bx_api_get_msg($sMsg, ['ext' => ['code' => 3, 'block_id' => $iBlockId]])] : ['code' => 3, 'actions' => ['ShowMsg'], 'block_id' => $iBlockId, 'msg' => $sMsg];
  566. self::onBlockDelete($iBlockId);
  567. return ($sMsg = _t('_sys_wiki_block_deleted')) && $this->_bIsApi ? [bx_api_get_msg($sMsg, ['ext' => ['code' => 0, 'block_id' => $iBlockId]])] : [
  568. 'code' => 0,
  569. 'actions' => ['DeleteBlock', 'ShowMsg'],
  570. 'block_id' => $iBlockId,
  571. 'msg' => $sMsg
  572. ];
  573. }
  574. public function actionHistory ($iBlockId = 0)
  575. {
  576. if(!$iBlockId && ($_iBlockId = bx_get('block_id')) !== false)
  577. $iBlockId = (int)$_iBlockId;
  578. $sLang = bx_lang_name();
  579. $oLang = BxDolLanguages::getInstance();
  580. $aVars = $this->getVarsForHistory($iBlockId, $sLang);
  581. if($this->_bIsApi)
  582. return ($aVersions = $aVars['bx_repeat:revisions'] ?: false) ? [bx_api_get_block('wiki_history', $aVersions)] : [bx_api_get_msg(_t('_sys_wiki_error_no_revs'), ['ext' => ['block_id' => $iBlockId]])];
  583. $aVars['language'] = $oLang->getLangTitle($oLang->getLangId($sLang));
  584. $aVars['close'] = _t('_sys_close');
  585. $aVars['msg'] = $aVars['bx_repeat:revisions'] ? '' : _t('_sys_wiki_error_no_revs');
  586. return BxDolTemplate::getInstance()->parseHtmlByName('wiki_history.html', $aVars);
  587. }
  588. public function actionTranslate ($iBlockId = 0)
  589. {
  590. return $this->actionEdit($iBlockId, true);
  591. }
  592. public function actionEdit ($iBlockId = 0, $bTranslate = false)
  593. {
  594. if(!$iBlockId && ($_iBlockId = bx_get('block_id')) !== false)
  595. $iBlockId = (int)$_iBlockId;
  596. $sMainLangLabel = '';
  597. $aWikiVerMain = [];
  598. $sLangForTranslate = '';
  599. $aLangsForInput = $this->getLangsForInput ($iBlockId, $bTranslate, $sMainLangLabel, $aWikiVerMain, $sLangForTranslate);
  600. // don't allow to translate empty blocks
  601. if (!$aWikiVerMain && $bTranslate)
  602. return ($sMsg = _t('_sys_wiki_error_no_revs')) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : BxDolTemplate::getInstance()->parseHtmlByName('wiki_msg.html', [
  603. 'close' => _t('_sys_close'),
  604. 'msg' => $sMsg,
  605. ]);
  606. // get latest revision for block with current lang
  607. $aWikiVer = call_user_func_array([$this->_oQuery, 'getBlockContent'], $bTranslate ? [$iBlockId, $sLangForTranslate, false, false] : [$iBlockId, bx_lang_name()]);
  608. if($aWikiVer) // unset notes since we need this field empty in the form
  609. unset($aWikiVer['notes']);
  610. if(!$aWikiVer) // check for new block, so initialize with default values
  611. $aWikiVer = [
  612. 'block_id' => $iBlockId,
  613. 'language' => $bTranslate ? $sLangForTranslate : bx_lang_name()
  614. ];
  615. // init form object
  616. $oForm = BxDolForm::getObjectInstance('sys_wiki', $bTranslate ? 'sys_wiki_translate' : 'sys_wiki_edit');
  617. if(!$oForm)
  618. return ($sMsg = _t('_sys_wiki_error_occured', 12)) && $this->_bIsApi ? [bx_api_get_msg($sMsg)] : $sMsg;
  619. $oForm->aInputs['content']['html_tags'] = true;
  620. if(isset($oForm->aInputs['language'])) {
  621. if($this->_bIsApi)
  622. foreach($aLangsForInput as $sKey => $aLangForInput)
  623. $oForm->aInputs['language']['values'][$sKey] = $aLangForInput['title'];
  624. else
  625. $oForm->aInputs['language']['values'] = $aLangsForInput;
  626. }
  627. if(($sK = 'content_main') && isset($oForm->aInputs[$sK]) && $sMainLangLabel)
  628. $oForm->aInputs[$sK] = array_merge($oForm->aInputs[$sK], [
  629. 'caption' => $sMainLangLabel,
  630. 'content' => $aWikiVerMain ? BxDolTemplate::getInstance()->parseHtmlByName('wiki_content.html', ['content' => $aWikiVerMain['content']]) : ''
  631. ]);
  632. if($this->_bIsApi) {
  633. if(($sK = 'content_main') && isset($oForm->aInputs[$sK]) && $sMainLangLabel) {
  634. $oForm->aInputs[$sK] = array_merge($oForm->aInputs[$sK], [
  635. 'type' => 'markdown',
  636. 'value' => $aWikiVerMain ? $aWikiVerMain['content'] : '',
  637. 'attrs' => [
  638. 'readonly' => true
  639. ]
  640. ]);
  641. unset($oForm->aInputs[$sK]['content']);
  642. }
  643. if(($sK = 'content') && isset($oForm->aInputs[$sK])) {
  644. $sFieldName = 'translations';
  645. $aValues = array_flip(array_keys($aLangsForInput));
  646. array_walk($aValues, function(&$mixedValue, $sKey) use($iBlockId) {
  647. $mixedValue = '';
  648. if(($aWikiVer = $this->_oQuery->getBlockContent($iBlockId, $sKey, false, false)) && is_array($aWikiVer))
  649. $mixedValue = $aWikiVer['content'];
  650. });
  651. foreach($aLangsForInput as $sName => $aInfo)
  652. $aLangsForInput[$sName]['name'] = $sFieldName . '-' . $aInfo['name'];
  653. $oForm->aInputs[$sK] = array_merge($oForm->aInputs[$sK], [
  654. 'type' => 'markdown_translatable',
  655. 'name' => $sFieldName,
  656. 'value' => '',
  657. 'values' => $aValues,
  658. 'translations' => array_values($aLangsForInput),
  659. 'db' => null
  660. ]);
  661. }
  662. unset($oForm->aInputs['language']);
  663. }
  664. $oForm->initChecker($aWikiVer);
  665. if (!$oForm->isSubmittedAndValid()) {
  666. // display form
  667. return $this->_bIsApi ? [bx_api_get_block('form', $oForm->getCodeAPI(), [
  668. 'ext' => [
  669. 'name' => $this->_sObject,
  670. 'request' => ['url' => '/api.php?r=system/wiki_action/TemplServiceWiki&params[]=' . $this->getUri() . '&params[]=' . ($bTranslate ? 'translate' : 'edit') . '&params[]=' . $iBlockId, 'immutable' => true]
  671. ]
  672. ])] : BxDolTemplate::getInstance()->parseHtmlByName('wiki_form.html', [
  673. 'form' => $oForm->getCode(),
  674. 'block_id' => $iBlockId,
  675. 'wiki_action_uri' => $this->getUri(),
  676. 'action' => $bTranslate ? 'translate' : 'edit',
  677. 'txt_open_editor' => bx_js_string(_t('_sys_wiki_open_in_editor')),
  678. ]);
  679. }
  680. else {
  681. if(($sLang = $oForm->getCleanValue('language')) !== false && ($sContent = $oForm->getCleanValue('content')) !== false)
  682. $sId = $this->_addBlockRevision($iBlockId, $sLang, $sContent, $oForm);
  683. // process translations if available
  684. $aTranslations = [];
  685. if(($sTranslations = bx_get('translations')) !== false)
  686. $aTranslations = json_decode($sTranslations, true);
  687. else
  688. foreach($aLangsForInput as $sName => $aInfo)
  689. if(($sTranslation = bx_get('translations-' . $sName)) !== false)
  690. $aTranslations[$sName] = $sTranslation;
  691. if($aTranslations && is_array($aTranslations))
  692. foreach ($aTranslations as $sLang => $sContent) {
  693. if($sLang == $oForm->getCleanValue('language'))
  694. continue;
  695. $sId = $this->_addBlockRevision($iBlockId, $sLang, $sContent, $oForm);
  696. }
  697. // update indexing data
  698. $this->updateBlockIndexingData($iBlockId);
  699. return $this->_bIsApi ? ['code' => 0, 'block_id' => $iBlockId] : ['code' => 0, 'actions' => ['Reload', 'ClosePopup'], 'block_id' => $iBlockId];
  700. }
  701. }
  702. protected function _addBlockRevision($iBlockId, $sLang, $sContent, &$oForm)
  703. {
  704. // get previous revision with priority for current language
  705. $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sLang);
  706. $bMainLang = $this->getFieldMainLangFlag ($oForm, $sLang, $aWikiVer);
  707. $sRev = $this->getFieldRev ($oForm, $sLang, $aWikiVer);
  708. $bUnsafe = $this->getFieldUnsafeFlag ($oForm, $sLang, $aWikiVer);
  709. // insert new revision (main lang is NOT allowed for translations)
  710. $iId = 0;
  711. if((!$bMainLang || ($bMainLang && $this->isAllowed('edit'))) && $this->isContentChanged($iBlockId, $sLang, $sContent)) {
  712. $iId = $oForm->insert([
  713. 'added' => time(),
  714. 'revision' => $sRev,
  715. 'main_lang' => $bMainLang,
  716. 'profile_id' => bx_get_logged_profile_id(),
  717. 'unsafe' => $bUnsafe,
  718. 'language' => $sLang,
  719. 'content' => $sContent,
  720. ]);
  721. }
  722. return $iId;
  723. }
  724. public function actionConvertLinks ()
  725. {
  726. $s = bx_get('s');
  727. if (!$s)
  728. return ['s' => ''];
  729. // convert links to be viewable in external editor
  730. $s = preg_replace_callback('#\((([a-zA-Z0-9_]+)/([a-zA-Z0-9]+))\)#', function ($aMatches) {
  731. $oStorage = BxDolStorage::getObjectInstance($aMatches[2]);
  732. if ($oStorage) {
  733. $sUrl = $oStorage->getFileUrlByRemoteId($aMatches[3]);
  734. if ($sUrl)
  735. return '(' . $sUrl . ')';
  736. }
  737. return '(' . $aMatches[0] . ')';
  738. }, $s);
  739. // add references to existing pages, so short references will work in external editor
  740. $s .= "\n<!-- " . _t("_sys_wiki_external_editor_references_comment") . " -->\n";
  741. $this->_aPages = $this->_oQuery->getAllPages ();
  742. if ($this->_aPages) {
  743. foreach ($this->_aPages as $sUri => $r)
  744. $s .= "[{$sUri}]: " . BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($r['url']) . "\n";
  745. }
  746. return ['s' => $s];
  747. }
  748. public function updateBlockIndexingData($iBlockId)
  749. {
  750. if (!($aLangs = $this->_oQuery->getBlockLangs ($iBlockId)))
  751. return false;
  752. $sText = '';
  753. foreach ($aLangs as $sLang) {
  754. $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sLang, false, false);
  755. $sText .= trim(strip_tags($aWikiVer['content'])) . ' ';
  756. }
  757. return $this->_oQuery->updateBlockIndexingData ($iBlockId, $sText);
  758. }
  759. protected function getPageUrl ($sPageUri, $bPermalink = true, $bAddRootUrl = true)
  760. {
  761. $sUrl = ($bAddRootUrl ? BX_DOL_URL_ROOT : '') . 'r.php?_q=' . $this->_aObject['uri'] . '/' . $sPageUri;
  762. return $bPermalink ? BxDolPermalinks::getInstance()->permalink($sUrl) : $sUrl;
  763. }
  764. protected function isContentChanged ($iBlockId, $sLang, $sContent)
  765. {
  766. if (!$sContent) // don't allow revisions with empty content
  767. return false;
  768. $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sLang, false, false);
  769. if (!$aWikiVer) // when no previous revision available
  770. return true;
  771. return $sContent != $aWikiVer['content'];
  772. }
  773. protected function getFieldRev ($oForm, $sLang, $aWikiVer) {
  774. // increase revision for particular language or start with first revision
  775. return $aWikiVer && $sLang == $aWikiVer['language'] ? $aWikiVer['revision'] + 1 : 1;
  776. }
  777. protected function getFieldMainLangFlag ($oForm, $sLang, $aWikiVer) {
  778. // detect main language flag
  779. if ($aWikiVer && $sLang == $aWikiVer['language'])
  780. return $aWikiVer['main_lang']; // when revision is increased (language matches) - copy this flag from previous revision
  781. elseif (!$aWikiVer)
  782. return 1; // when it's first revision and no translations - start as main language
  783. else
  784. return 0;
  785. }
  786. protected function getFieldUnsafeFlag ($oForm, $sLang, $aWikiVer) {
  787. if ($aWikiVer && $sLang == $aWikiVer['language'] && !$aWikiVer['unsafe'])
  788. return 0; // copy 'unsafe' from previous revision only when unsafe = 0
  789. else
  790. return $this->isAllowed('unsafe') ? 1 : 0; // in other cases - check permissions
  791. }
  792. protected function getLangsForInput ($iBlockId, $bTranslateForm, &$sMainLangLabel, &$aWikiVerMain, &$sLangForTranslate)
  793. {
  794. // get main lang update time
  795. $aWikiVerMain = $this->_oQuery->getBlockContent ($iBlockId, 'neverhood');
  796. $iUpdatedMainLang = $aWikiVerMain ? $aWikiVerMain['added'] : 0;
  797. // generate values for radio set
  798. $aLangs = BxDolLanguages::getInstance()->getLanguages(false, true);
  799. foreach ($aLangs as $sKey => $sLang) {
  800. $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sKey, false, false);
  801. $bWikiVer = $aWikiVer && is_array($aWikiVer);
  802. $bOutdatedLang = !$bWikiVer || $iUpdatedMainLang > $aWikiVer['added'];
  803. $bMainLang = $bWikiVer && $aWikiVer['main_lang'];
  804. $sMainLang = $bMainLang ? '★' : '';
  805. $sComment = $bWikiVer ? bx_time_js($aWikiVer['added']) : _t('_sys_wiki_lang_missing');
  806. $aLangs[$sKey] = $this->_bIsApi ? [
  807. 'name' => $sKey,
  808. 'title' => $sLang,
  809. 'added' => $bWikiVer ? $aWikiVer['added'] : '',
  810. 'main' => $bMainLang,
  811. 'outdated' => $bOutdatedLang
  812. ] : _t('_sys_wiki_lang_mask' . ($bOutdatedLang ? '_warn' : ''), $sLang, $sMainLang, $sComment);
  813. if($bTranslateForm) {
  814. if(!$bMainLang && !$sLangForTranslate)
  815. $sLangForTranslate = $sKey;
  816. if($bMainLang) {
  817. $sMainLangLabel = $this->_bIsApi ? $aLangs[$sKey]['title'] : $aLangs[$sKey];
  818. unset($aLangs[$sKey]);
  819. continue;
  820. }
  821. }
  822. }
  823. return $aLangs;
  824. }
  825. public function getVarsForHistory ($iBlockId, $sLang)
  826. {
  827. $sUrl = '';
  828. if(!($aPage = $this->_oQuery->getPageByBlockId($iBlockId))) {
  829. list($sPageLink, $aPageParams) = bx_get_base_url_popup();
  830. $sUrl = bx_absolute_url(BxDolPermalinks::getInstance()->permalink(bx_append_url_params($sPageLink, $aPageParams)));
  831. }
  832. else
  833. $sUrl = $this->getPageUrl($aPage['uri']);
  834. $a = $this->_oQuery->getBlockHistory($iBlockId, $sLang);
  835. $aVars = [];
  836. foreach ($a as $r) {
  837. $oProfile = BxDolProfile::getInstanceMagic($r['profile_id']);
  838. $sUrlRef = bx_append_url_params($sUrl, [$r['block_id'] . 'rev' => $r['revision']]);
  839. $aVars[] = array_merge($r, $this->_bIsApi ? [
  840. 'author_data' => BxDolProfile::getData($oProfile),
  841. 'rev_url' => bx_api_get_relative_url($sUrlRef)
  842. ] : [
  843. 'author_name' => $oProfile->getDisplayName(),
  844. 'author_url' => $oProfile->getUrl(),
  845. 'timejs' => bx_time_js($r['added']),
  846. 'rev_url' => $sUrlRef
  847. ]);
  848. }
  849. return [
  850. 'bx_repeat:revisions' => $aVars
  851. ];
  852. }
  853. }
  854. class BxDolParsedown extends Parsedown
  855. {
  856. protected $_aPages;
  857. protected function textElements($text)
  858. {
  859. $res = parent::textElements($text);
  860. // add all available pages as references
  861. $this->_aPages = BxDolWikiQuery::getAllPages ();
  862. if ($this->_aPages) {
  863. foreach ($this->_aPages as $sUri => $r) {
  864. $aData = array(
  865. // real link URL is set later when link is referenced
  866. 'url' => 'bx-internal-page://' . $sUri,
  867. 'title' => !empty($r['title']) ? _t($r['title']) : null,
  868. );
  869. $this->DefinitionData['Reference'][$sUri] = $aData;
  870. }
  871. }
  872. return $res;
  873. }
  874. protected function inlineLink($Excerpt)
  875. {
  876. $a = parent::inlineLink($Excerpt);
  877. if ($a && @isset($a['element']['attributes']['href']) && 0 === strncmp($a['element']['attributes']['href'], 'bx-internal-page://', 19)) {
  878. $sUri = strtolower(substr($a['element']['attributes']['href'], 19));
  879. if (isset($this->_aPages[$sUri])) {
  880. $sHref = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($this->_aPages[$sUri]['url']);
  881. $a['element']['attributes']['href'] = $sHref;
  882. }
  883. else {
  884. $a['element']['attributes']['href'] = $sUri;
  885. }
  886. }
  887. if ($a && @isset($a['element']['attributes']['title']) && '#' === $a['element']['attributes']['title']) {
  888. $a['element']['attributes']['class'] = 'bx-def-font-grayed';
  889. }
  890. return $a;
  891. }
  892. protected function inlineImage($Excerpt)
  893. {
  894. $a = parent::inlineImage($Excerpt);
  895. if (@isset($a['element']['attributes']['src']) && preg_match('#^([a-zA-Z0-9_]+)/([a-zA-Z0-9]+)$#', $a['element']['attributes']['src'], $aMatches) && is_array($aMatches) && 3 == count($aMatches)) {
  896. $oStorage = BxDolStorage::getObjectInstance($aMatches[1]);
  897. if ($oStorage) {
  898. $sUrl = $oStorage->getFileUrlByRemoteId($aMatches[2]);
  899. if ($sUrl)
  900. $a['element']['attributes']['src'] = $sUrl;
  901. }
  902. }
  903. return $a;
  904. }
  905. protected function blockHeader($Excerpt) {
  906. $a = parent::blockHeader($Excerpt);
  907. if (isset($a)) {
  908. $sText = trim($a['element']['handler']['argument'], '#');
  909. $sText = trim($sText, ' ');
  910. $sLink = preg_replace('/[^\p{L}\p{N}\p{M}-]+/u', '', mb_strtolower(mb_ereg_replace(' ','-', $sText)));
  911. $aAttr = array();
  912. if (!empty($sLink)) {
  913. $aAttr = isset($a['element']['attributes']) ? $a['element']['attributes'] : [];
  914. $aAttr['id'] = $sLink;
  915. $aHandler = array(
  916. 'function' => 'lineElements',
  917. 'argument' => $sText . (isset($_SERVER['REQUEST_URI']) ? ' [](' . bx_get_self_url() . '#' . $sLink . ' "#")' : ''),
  918. 'destination' => 'elements',
  919. );
  920. $a['element']['attributes'] = $aAttr;
  921. $a['element']['handler'] = $aHandler;
  922. }
  923. }
  924. else {
  925. $a = null;
  926. }
  927. return $a;
  928. }
  929. }
  930. /** @} */