Alex Trofimov 4 лет назад
Родитель
Сommit
55e304d812

+ 10 - 5
inc/classes/BxDolWiki.php

@@ -325,6 +325,12 @@ class BxDolWiki extends BxDolFactory implements iBxDolFactoryObject
         return $aCheck[CHECK_ACTION_RESULT] === CHECK_ACTION_RESULT_ALLOWED;
     }
 
+    public function addPage ($sPageUri, $sTitleLangKey)
+    {
+        $sUrl = $this->getPageUrl($sPageUri, false, false);
+        return $this->_oQuery->insertPage ($sPageUri, $sUrl, $sTitleLangKey);
+    }
+
     public function actionGetTranslation ()
     {
         $aWikiVer = $this->_oQuery->getBlockContent ((int)bx_get('block_id'), bx_get('language'), false, false);
@@ -500,8 +506,7 @@ class BxDolWiki extends BxDolFactory implements iBxDolFactoryObject
 
             // insert page
             $sLangKey = '_' . $this->_aObject['module'] . '_' . str_replace('-', '_', $sPageUri) . '_' . time();
-            $sUrl = $this->getPageUrl($sPageUri, false, false);
-            $iPageId = $this->_oQuery->insertPage ($sPageUri, $sUrl, $sLangKey);
+            $iPageId = $this->addPage ($sPageUri, $sLangKey);
             if (!$iPageId)
                 return array('code' => 10, 'actions' => array('ShowMsg'), 'msg' => _t('_sys_wiki_error_occured', 10));
 
@@ -730,7 +735,7 @@ class BxDolWiki extends BxDolFactory implements iBxDolFactoryObject
         return ['s' => $s];
     }
 
-    protected function updateBlockIndexingData($iBlockId)
+    public function updateBlockIndexingData($iBlockId)
     {
         if (!($aLangs = $this->_oQuery->getBlockLangs ($iBlockId)))
             return false;
@@ -862,8 +867,8 @@ class BxDolParsedown extends Parsedown
     {
         $a = parent::inlineLink($Excerpt);
 
-        if (@isset($a['element']['attributes']['href']) && 0 === strncmp($a['element']['attributes']['href'], 'bx-internal-page://', 19)) {
-            $sUri = substr($a['element']['attributes']['href'], 19);
+        if ($a && @isset($a['element']['attributes']['href']) && 0 === strncmp($a['element']['attributes']['href'], 'bx-internal-page://', 19)) {
+            $sUri = strtolower(substr($a['element']['attributes']['href'], 19));
             if (isset($this->_aPages[$sUri])) {
                 $sHref = BX_DOL_URL_ROOT . BxDolPermalinks::getInstance()->permalink($this->_aPages[$sUri]['url']);
                 $a['element']['attributes']['href'] = $sHref;

+ 1 - 1
inc/classes/BxDolWikiQuery.php

@@ -44,7 +44,7 @@ class BxDolWikiQuery extends BxDolDb
 
     static public function getAllPages ()
     {
-        $sQuery = "SELECT `uri`, `title`, `url` FROM `sys_objects_page` WHERE `url` != '' ORDER BY `uri`";
+        $sQuery = "SELECT LOWER(`uri`) as `uri`, `title`, `url` FROM `sys_objects_page` WHERE `url` != '' ORDER BY `uri`";
         return BxDolDb::getInstance()->getAllWithKey($sQuery, 'uri');
     }
 

+ 335 - 0
modules/boonex/wiki/classes/BxWikiStudioPage.php

@@ -14,7 +14,342 @@ class BxWikiStudioPage extends BxTemplStudioModule
     function __construct($sModule, $mixedPageName, $sPage = "")
     {
         parent::__construct($sModule, $mixedPageName, $sPage);
+
+        $this->aMenuItems = array(
+            'settings' => ['name' => 'settings', 'icon' => 'cogs', 'title' => '_adm_lmi_cpt_settings'],
+            'import' => ['name' => 'import', 'icon' => 'fab github', 'title' => '_bx_wiki_import'],
+        );
+    }
+
+    function getImport ()
+    {
+        $aForm = array(
+            'form_attrs' => array(
+                'name' => 'bx-wiki-import',
+                'method' => 'post',
+            ),
+            'params' => array(
+                'db' => array(
+                    'submit_name' => 'do_submit',
+                ),
+            ),
+            'inputs' => array(
+                'repo' => array(
+                    'type' => 'text',
+                    'name' => 'repo',
+                    'caption' => _t('_bx_wiki_repository_url'),
+                    'required' => true,
+                ),
+                'base_url' => array(
+                    'type' => 'text',
+                    'name' => 'base_url',
+                    'caption' => _t('_bx_wiki_base_url'),
+                    'required' => true,
+                ),
+                'lang' => array(
+                    'type' => 'select',
+                    'name' => 'lang',
+                    'caption' => _t('_bx_wiki_lang'),
+                    'values' => BxDolLanguages::getInstance()->getLanguages(),
+                    'value' => BxDolLanguages::getInstance()->getCurrentLanguage(),
+                ),
+                'unsafe' => array(
+                    'type' => 'checkbox',
+                    'name' => 'unsafe',
+                    'caption' => _t('_bx_wiki_unsafe'),
+                ),
+                'add_titles' => array(
+                    'type' => 'checkbox',
+                    'name' => 'add_titles',
+                    'caption' => _t('_bx_wiki_add_titles'),
+                ),
+                'git' => array(
+                    'type' => 'text',
+                    'name' => 'git',
+                    'caption' => _t('_bx_wiki_repository_git'),
+                    'required' => true,
+                    'value' => 'git',
+                ),
+                'ext' => array(
+                    'type' => 'text',
+                    'name' => 'ext',
+                    'caption' => _t('_bx_wiki_ext'),
+                    'required' => true,
+                    'value' => 'md',
+                ),
+                'skip_files' => array(
+                    'type' => 'textarea',
+                    'name' => 'skip_files',
+                    'caption' => _t('_bx_wiki_skip_files'),
+                    'value' => '["_Footer.md"]',
+                ),
+                'only_files' => array(
+                    'type' => 'textarea',
+                    'name' => 'only_files',
+                    'caption' => _t('_bx_wiki_only_files'),
+                ),
+                'page_uri_map' => array(
+                    'type' => 'textarea',
+                    'name' => 'page_uri_map',
+                    'caption' => _t('_bx_wiki_page_uri_map'),
+                    'value' => '{"Home": "Index", "Dashboard": "Wiki-Dashboard"}',
+                ),
+                'submit' => array(
+                    'type' => 'submit',
+                    'name' => 'do_submit', // the same as key and database field name
+                    'value' => _t('_bx_wiki_import'),
+                ),
+            ),
+        );
+        $oForm = new BxTemplFormView($aForm);
+        $oForm->initChecker();
+        if ($oForm->isSubmittedAndValid ()) {
+            $sGit = 
+            $sRepoUrl = 
+            $aParams = [
+                'git' => $oForm->getCleanValue('git'),
+                'repository' => $oForm->getCleanValue('repo'),
+                'base_url' => $oForm->getCleanValue('base_url'),
+                'lang' => $oForm->getCleanValue('lang'),
+                'unsafe' => (boolean)$oForm->getCleanValue('unsafe'),
+                'add_titles' => (boolean)$oForm->getCleanValue('add_titles'),
+                'ext' => $oForm->getCleanValue('ext'),
+                'skip_files' => json_decode($oForm->getCleanValue('skip_files'), true),
+                'only_files' => json_decode($oForm->getCleanValue('only_files'), true),
+                'page_uri_map' => json_decode($oForm->getCleanValue('page_uri_map'), true),
+            ];
+            $sErrorMsg = '';
+            $b = $this->import($aParams, $sErrorMsg);
+            if ($b)
+                return _t('_bx_wiki_import_success');
+            else
+                return $sErrorMsg;
+        }
+        else {
+            return $oForm->getCode();
+        }
+    }
+
+    static public function import($aParams, &$sErrorMsg) 
+    {
+        $sGit = $aParams['git'];
+        $sRepoUrl = $aParams['repository'];
+        $sExt = $aParams['ext'];
+        $aSkipFiles = $aParams['skip_files'];
+        $aOnlyFiles = $aParams['only_files'];
+        $sErrorMsg = '';
+
+        // empty directory to clone repository to
+        $sDir = BX_DIRECTORY_PATH_TMP . 'bx_wiki_import';
+        if (file_exists($sDir) && !bx_rrmdir($sDir)) {
+            $sErrorMsg = "$sDir can't be removed";
+            return false;
+        }
+
+        // clone repository
+        $sCmd = "{$sGit} clone {$sRepoUrl} " . $sDir;
+        $aOutput = null;
+        $iRetVal = null;
+        if (false === exec(escapeshellcmd($sCmd), $aOutput, $iRetVal) || (null !== $iRetVal && $iRetVal)) {
+            $sErrorMsg = "Command($sCmd) execution failed";
+            return false;
+        }
+
+        if (false === ($h = opendir($sDir))) {
+            $sErrorMsg = "Directory($sDir) read failed";
+            return false;
+        }
+
+        // get list of files to import and check pages for existence
+        $aFilesToImport = [];
+        while (false !== ($sFile = readdir($h))) {
+            if (!empty($aSkipFiles) && in_array($sFile, $aSkipFiles))
+                continue;
+
+            if (!empty($aOnlyFiles) && !in_array($sFile, $aOnlyFiles))
+                continue;
+
+            $a = pathinfo($sDir . '/' . $sFile);
+            if (empty($a['filename']) || empty($a['extension']) || $a['extension'] != $sExt)
+                continue;
+
+            $sErrorMsgFile = '';
+            if (self::isPageExist($aParams, $sDir . '/' . $sFile, $a['filename'], $sErrorMsgFile)) {
+                $sErrorMsg = "File($sFile) import failed: $sErrorMsgFile";
+                break;
+            }
+
+            $aFilesToImport[$sFile] = $a;
+        }
+        closedir($h);
+
+        if ($sErrorMsg)
+            return false;
+
+        // perform import
+        foreach ($aFilesToImport as $sFile => $a) {
+            $sErrorMsgFile = '';
+            if (!self::importFile($aParams, $sDir . '/' . $sFile, $a['filename'], $sErrorMsgFile)) {
+                $sErrorMsg = "File($sFile) import failed: $sErrorMsgFile";
+                break;
+            }
+        }
+        if ($sErrorMsg)
+            return false;
+
+        return true;
+    }
+
+    static protected function isPageExist($aParams, $sPath, $sTitle, &$sErrorMsg)
+    {
+        $aMapPageUris = $aParams['page_uri_map'];
+        $sUri = $sTitle;
+        if (isset($aMapPageUris[$sUri]))
+            $sUri = $aMapPageUris[$sUri];
+        if ($oPage = BxDolPage::getObjectInstanceByURI($sUri)) {
+            $sErrorMsg = "Page with such URI($sUri) already exists";
+            return true;
+        }
+        return false;
     }
+
+    static protected function importFile($aParams, $sPath, $sTitle, &$sErrorMsg)
+    {
+        $sBaseUrlOld = $aParams['base_url'];
+        $sLang = $aParams['lang'];
+        $bUnsafe = $aParams['unsafe'];
+        $bAddTitles = $aParams['add_titles'];
+        $aMapPageUris = $aParams['page_uri_map'];
+
+        if (false === ($sContents = file_get_contents($sPath)) || empty($sContents)) {
+            $sErrorMsg = "File($sPath) read failed";
+            return false;
+        }    
+
+        $oWiki = BxDolWiki::getObjectInstance('bx_wiki');
+
+        $sUri = $sTitle;
+        if (isset($aMapPageUris[$sUri]))
+            $sUri = $aMapPageUris[$sUri];
+
+        $sTitle = str_replace('-', ' ', $sTitle);
+
+        if (!($iPageId = $oWiki->addPage($sUri, $sTitle))) {
+            $sErrorMsg = "Page with URI($sUri) add failed";
+            return false;
+        }
+                       
+        if (!($oPage = BxDolPage::getObjectInstanceByURI($sUri))) {
+            $sErrorMsg = "Page with URI($sUri) wasn't found";
+            return false;
+        }
+
+        $iDesignBox = getParam('bx_wiki_design_box');
+        $aBlock = array(
+            'object' => $oPage->getName(),
+            'cell_id' => 1,
+            'module' => 'bx_wiki',
+            'title_system' => '',
+            'title' => '',
+            'designbox_id' => false === $iDesignBox ? 0 : $iDesignBox,
+            'visible_for_levels' => 2147483647, 
+            'type' => 'wiki',
+            'deletable' => 1,
+            'copyable' => 0,
+            'active' => 1,
+        );
+        $oQueryPageBuilder = new BxDolStudioBuilderPageQuery();
+        if (!($iBlockId = $oQueryPageBuilder->insertBlock($aBlock))) {
+            $sErrorMsg = "Page(" . $oPage->getName() . ") block add failed";
+            return false;
+        }
+
+        $sContents = self::processImages($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId);
+        $sContents = self::processContents($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId);
+
+        if ($bAddTitles)
+            $sContents = "# $sTitle\n\n" . $sContents;
+
+        $oForm = BxDolForm::getObjectInstance('sys_wiki', 'sys_wiki_edit');
+        $sIdRevision = $oForm->insert(array(
+            'block_id' => $iBlockId,
+            'revision' => 1,
+            'language' => $sLang,
+            'main_lang' => 1,
+            'profile_id' => 0,
+            'content' => $sContents,
+            'unsafe' => $bUnsafe,
+            'notes' => 'Import',
+            'added' => time(), 
+        ));
+        if (!$sIdRevision) {
+            $sErrorMsg = "Revision add failed for block ($iBlockId)";
+            return false;
+        }
+
+        $oWiki->updateBlockIndexingData($iBlockId);
+
+        return true;
+    }
+
+    static protected function processContents($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId)
+    {
+        // strip base URL
+        $sContents = preg_replace("/\(" . preg_quote($sBaseUrlOld, '/') . "([^\)]+)\)/", "[$1]", $sContents);
+
+        // replace special constructions which don't work locally
+        $sContents = preg_replace('/\[\[(.+?)\|(.+?)\]\]/', '[$1][$2]', $sContents);
+        $sContents = preg_replace('/\[(.+?)\|(.+?)\]/', '[$1][$2]', $sContents);
+
+        // replace old URIs with new ones
+        foreach ($aMapPageUris as $sOldUri => $sNewUri)
+            $sContents = str_replace("[$sOldUri]", "[$sNewUri]", $sContents);
+
+        // convert links to the proper ones
+        $sContents = preg_replace('/\[(.+?)\]\[(http:\/\/.+?)\]/', '[$1]($2)', $sContents);
+        $sContents = preg_replace('/\[(.+?)\]\[(https:\/\/.+?)\]/', '[$1]($2)', $sContents);
+
+        return $sContents;
+    }
+
+    static protected function processImages($sContents, $aMapPageUris, $sBaseUrlOld, $iBlockId)
+    {
+        // unify links to images
+        $sContents = preg_replace('/\[\[(.+?)\|alt=(.+?)\]\]/', '![$2]($1)', $sContents);
+
+        // process images
+        $sContents = preg_replace_callback('/\!\[(.+?)\]\((.+?)\)/', function ($aMatches) use ($sBaseUrlOld, $iBlockId) {
+            if (empty($aMatches[2]))
+                return $aMatches[0];
+
+            // strip base Wiki URL
+            $sImg = str_replace($sBaseUrlOld, "", $aMatches[2]);
+
+            // skip external URLs
+            if (0 === strncmp($sImg, 'http://', 7) || 0 === strncmp($sImg, 'https://', 8))
+                return $aMatches[0];
+
+            $sImgPath = BX_DIRECTORY_PATH_TMP . 'bx_wiki_import/' . ltrim($sImg, '/');
+            if (!file_exists($sImgPath))
+                return $aMatches[0];
+
+            $oStorage = BxDolStorage::getObjectInstance('sys_wiki_files');
+            if (!$oStorage)
+                return $aMatches[0];
+
+            if (!($iFileId = $oStorage->storeFileFromPath($sImgPath, false, 0, $iBlockId)))
+                return $aMatches[0];
+
+            if (!($aFile = $oStorage->getFile($iFileId)))
+                return $aMatches[0];
+
+            return "![{$aMatches[2]}](" . $oStorage->getObject() . "/" . $aFile['remote_id'] . ")";
+ 
+        }, $sContents);
+
+        return $sContents;
+    }
+
 }
 
 /** @} */

+ 12 - 0
modules/boonex/wiki/install/langs/en.xml

@@ -14,6 +14,18 @@
     <string name="_bx_wiki_block_outdated_translations"><![CDATA[Outdated Translations]]></string>
     <string name="_bx_wiki_menu_item_title_system_home"><![CDATA[Wiki]]></string>
     <string name="_bx_wiki_option_design_box"><![CDATA[Default design box for new block]]></string>
+    <string name="_bx_wiki_import"><![CDATA[Import]]></string>
+    <string name="_bx_wiki_repository_url"><![CDATA[Repository URL]]></string>
+    <string name="_bx_wiki_base_url"><![CDATA[Base URL for old wiki]]></string>
+    <string name="_bx_wiki_lang"><![CDATA[Language to import to]]></string>
+    <string name="_bx_wiki_unsafe"><![CDATA[Allow unsafe HTML]]></string>
+    <string name="_bx_wiki_add_titles"><![CDATA[Add page title to the beginning of wiki content]]></string>
+    <string name="_bx_wiki_repository_git"><![CDATA[Git command path]]></string>
+    <string name="_bx_wiki_ext"><![CDATA[File extensions to import content from]]></string>
+    <string name="_bx_wiki_skip_files"><![CDATA[Skip file names (JSON encoded string)]]></string>
+    <string name="_bx_wiki_only_files"><![CDATA[Import only listed files (JSON encoded string)]]></string>
+    <string name="_bx_wiki_page_uri_map"><![CDATA[Convert page URIs (helpful when some page already exists and it needs to be imported with different URI)]]></string>
+    <string name="_bx_wiki_import_success"><![CDATA[Wiki pages were successfully imported]]></string>
 	<string name="_bx_wiki_acl_action_add_page"><![CDATA[Add Page]]></string>
 	<string name="_bx_wiki_acl_action_add_block"><![CDATA[Add Block]]></string>
 	<string name="_bx_wiki_acl_action_edit_block"><![CDATA[Edit Block]]></string>