Kaynağa Gözat

#3613 Wiki: ability to insert images

Alex Trofimov 4 yıl önce
ebeveyn
işleme
4df66c0c44

+ 1 - 0
inc/classes/BxDolUploader.php

@@ -335,6 +335,7 @@ abstract class BxDolUploader extends BxDolFactory
                 'file_title' => $oStorage->getFileTitle($aFile['file_name']),
                 'file_icon' => $sFileIcon,
                 'file_url' => $oStorage->getFileUrlById($aFile['id']),
+                'file_remote_id' => $aFile['remote_id'],
                 'js_instance_name' => $this->_sUploaderJsInstance,
             );
 

+ 20 - 1
inc/classes/BxDolWiki.php

@@ -128,7 +128,7 @@ class BxDolWiki extends BxDolFactory implements iBxDolFactoryObject
         $aWikiVer = $this->_oQuery->getBlockContent ($iBlockId, $sLang, $iRevision);
         $aWikiLatest = $this->_oQuery->getBlockContent ($iBlockId, $sLang);
         if ($aWikiVer) {
-            $oParsedown = new Parsedown();
+            $oParsedown = new BxDolParsedown();
             $oParsedown->setSafeMode($aWikiVer['unsafe'] ? false : true);
             $s = $oParsedown->text($aWikiVer['content']);
         }
@@ -798,4 +798,23 @@ class BxDolWiki extends BxDolFactory implements iBxDolFactoryObject
     }
 }
 
+class BxDolParsedown extends Parsedown
+{
+    protected function inlineImage($Excerpt)
+    {
+        $a = parent::inlineImage($Excerpt);
+
+        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)) {
+
+            $oStorage = BxDolStorage::getObjectInstance($aMatches[1]);
+            if ($oStorage) {
+                $sUrl = $oStorage->getFileUrlByRemoteId($aMatches[2]);
+                if ($sUrl)
+                    $a['element']['attributes']['src'] = $sUrl;
+            }
+        }
+        return $a;
+    }
+}
+
 /** @} */

+ 32 - 0
inc/js/classes/BxDolWiki.js

@@ -187,4 +187,36 @@ function bx_wiki_add_block(e, sPage, iCellId, sActionUri) {
     }, 'json');
 }
 
+function bx_wiki_insert_img (sEditorId, sImgUrl, sImgName) {
+    var s = "![" + sImgName + "](" + sImgUrl + ")";
+    var e = $(sEditorId).size() ? $(sEditorId).get(0) : false;
+    
+    if (!e)
+        return;
+
+    if (e.setRangeText) {
+        e.setRangeText(s);
+    } 
+    else {
+        e.focus();
+        document.execCommand('insertText', false, s);
+    }
+}
+
+function bx_wiki_remove_img (sEditorId, sImgUrl, sImgName) {
+    var sVal = $(sEditorId).val();
+    if (!sVal)
+        return;
+
+    var sImg = "![IMGNAMEHERE](" + sImgUrl + ")";
+    sImg = sImg.replace(/[|\\{}()[\]^$+*?.]/mg, '\\$&').replace(/-/mg, '\\x2d'); // escape special chars
+    sImg = sImg.replace(/IMGNAMEHERE/, '(.*?)');
+
+    var re = new RegExp(sImg, 'gm');
+    sVal = sVal.replace(re, '');
+
+    $(sEditorId).val(sVal);
+}
+
+
 /** @} */

+ 32 - 0
inc/js/functions.js

@@ -1263,4 +1263,36 @@ function bx_is_selector_in_stylesheet(sSel)
     return jQuery.isFunction(o.includes) && o.includes('.plyr');
 }
 
+function bx_copy_to_clipboard(s, onSuccess, onFail)
+{
+    if ("undefined" === typeof(onSuccess))
+        onSuccess = function (){};
+    if ("undefined" === typeof(onFail))
+        onFail = function (){};
+
+    const oClip = navigator.clipboard;
+    if ("undefined" !== typeof(oClip)) {
+        navigator.clipboard.writeText(s).then(onSuccess, onFail);
+    } 
+    else {
+        if (document.execCommand) {
+            const el = document.createElement("input");
+            el.value = s;
+            document.body.append(el);
+
+            el.select();
+            el.setSelectionRange(0, s.length);
+
+            if (document.execCommand("copy")) {
+                onSuccess();
+            }
+
+            el.remove();
+        }
+        else {
+            onFail();
+        }
+    }
+}
+
 /** @} */

+ 29 - 9
install/sql/system.sql

@@ -1484,6 +1484,22 @@ CREATE TABLE `sys_images_editor_resized` (
   UNIQUE KEY `remote_id` (`remote_id`)
 );
 
+CREATE TABLE IF NOT EXISTS `sys_wiki_files` (
+  `id` int(11) NOT NULL AUTO_INCREMENT,
+  `profile_id` int(10) unsigned NOT NULL,
+  `remote_id` varchar(128) NOT NULL,
+  `path` varchar(255) NOT NULL,
+  `file_name` varchar(255) NOT NULL,
+  `mime_type` varchar(128) NOT NULL,
+  `ext` varchar(32) NOT NULL,
+  `size` bigint(20) NOT NULL,
+  `added` int(11) NOT NULL,
+  `modified` int(11) NOT NULL,
+  `private` int(11) NOT NULL,
+  PRIMARY KEY (`id`),
+  UNIQUE KEY `remote_id` (`remote_id`)
+);
+
 -- --------------------------------------------------------
 
 
@@ -2108,7 +2124,8 @@ INSERT INTO `sys_objects_storage` (`object`, `engine`, `params`, `token_life`, `
 ('sys_transcoder_queue_files', 'Local', '', 3600, 2592000, 0, 'sys_transcoder_queue_files', 'allow-deny', 'avi,flv,mpg,mpeg,wmv,mp4,m4v,mov,divx,xvid,3gp,webm,jpg', '', 0, 0, 0, 0, 0, 0),
 ('sys_files', 'Local', '', 360, 2592000, 3, 'sys_files', 'deny-allow', '', 'action,apk,app,bat,bin,cmd,com,command,cpl,csh,exe,gadget,inf,ins,inx,ipa,isu,job,jse,ksh,lnk,msc,msi,msp,mst,osx,out,paf,pif,prg,ps1,reg,rgs,run,sct,shb,shs,u3p,vb,vbe,vbs,vbscript,workflow,ws,wsf', 0, 0, 0, 0, 0, 0),
 ('sys_images_editor', 'Local', '', 360, 2592000, 3, 'sys_images_editor', 'allow-deny', 'jpg,jpeg,jpe,gif,png', '', 0, 0, 0, 0, 0, 0),
-('sys_images_editor_resized', 'Local', '', 360, 2592000, 3, 'sys_images_editor_resized', 'allow-deny', 'jpg,jpeg,jpe,gif,png', '', 0, 0, 0, 0, 0, 0);
+('sys_images_editor_resized', 'Local', '', 360, 2592000, 3, 'sys_images_editor_resized', 'allow-deny', 'jpg,jpeg,jpe,gif,png', '', 0, 0, 0, 0, 0, 0),
+('sys_wiki_files', 'Local', '', 360, 2592000, 3, 'sys_wiki_files', 'allow-deny', 'jpg,jpeg,jpe,gif,png', '', 0, 0, 0, 0, 0, 0);
 
 CREATE TABLE IF NOT EXISTS `sys_storage_user_quotas` (
   `profile_id` int(11) NOT NULL,
@@ -3444,6 +3461,7 @@ INSERT INTO `sys_form_inputs` (`object`, `module`, `name`, `value`, `values`, `c
 ('sys_wiki', 'system', 'content_main', '', '', 0, 'custom', '', '_sys_form_wiki_input_caption_content_main', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('sys_wiki', 'system', 'content', '', '', 0, 'textarea', '', '_sys_form_wiki_input_caption_content', '_sys_form_wiki_input_caption_content_info', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
 ('sys_wiki', 'system', 'notes', '', '', 0, 'text', '', '_sys_form_wiki_input_caption_notes', '', 0, 0, 0, '', '', '', '', '', '', 'Xss', '', 1, 0),
+('sys_wiki', 'system', 'files', 'a:1:{i:0;s:9:"sys_html5";}', 'a:1:{s:9:"sys_html5";s:25:"_sys_uploader_html5_title";}', 0, 'files', '', '_sys_form_wiki_input_caption_files', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
 ('sys_wiki', 'system', 'do_submit', '_sys_submit', '', 0, 'submit', '_sys_form_wiki_input_caption_submit', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 0, 0),
 ('sys_wiki', 'system', 'close', '_sys_close', '', 0, 'reset', '_sys_form_wiki_input_caption_close', '', '', 0, 0, 0, 'a:2:{s:7:\"onclick\";s:46:\"$(\'.bx-popup-applied:visible\').dolPopupHide();\";s:5:\"class\";s:22:\"bx-def-margin-sec-left\";}', '', '', '', '', '', '', '', 1, 0),
 ('sys_wiki', 'system', 'buttons', '', 'do_submit,close', 0, 'input_set', '_sys_form_wiki_buttons', '', '', 0, 0, 0, '', '', '', '', '', '', '', '', 1, 0),
@@ -3638,19 +3656,21 @@ INSERT INTO `sys_form_display_inputs` (`display_name`, `input_name`, `visible_fo
 ('sys_wiki_edit', 'block_id', 2147483647, 1, 1),
 ('sys_wiki_edit', 'language', 2147483647, 1, 2),
 ('sys_wiki_edit', 'content', 2147483647, 1, 3),
-('sys_wiki_edit', 'notes', 2147483647, 1, 4),
-('sys_wiki_edit', 'do_submit', 2147483647, 1, 5),
-('sys_wiki_edit', 'close', 2147483647, 1, 6),
-('sys_wiki_edit', 'buttons', 2147483647, 1, 7),
+('sys_wiki_edit', 'files', 2147483647, 1, 4),
+('sys_wiki_edit', 'notes', 2147483647, 1, 5),
+('sys_wiki_edit', 'do_submit', 2147483647, 1, 6),
+('sys_wiki_edit', 'close', 2147483647, 1, 7),
+('sys_wiki_edit', 'buttons', 2147483647, 1, 8),
 
 ('sys_wiki_translate', 'block_id', 2147483647, 1, 1),
 ('sys_wiki_translate', 'content_main', 2147483647, 1, 2),
 ('sys_wiki_translate', 'language', 2147483647, 1, 3),
 ('sys_wiki_translate', 'content', 2147483647, 1, 4),
-('sys_wiki_translate', 'notes', 2147483647, 1, 5),
-('sys_wiki_translate', 'do_submit', 2147483647, 1, 6),
-('sys_wiki_translate', 'close', 2147483647, 1, 7),
-('sys_wiki_translate', 'buttons', 2147483647, 1, 8),
+('sys_wiki_translate', 'files', 2147483647, 1, 5),
+('sys_wiki_translate', 'notes', 2147483647, 1, 6),
+('sys_wiki_translate', 'do_submit', 2147483647, 1, 7),
+('sys_wiki_translate', 'close', 2147483647, 1, 8),
+('sys_wiki_translate', 'buttons', 2147483647, 1, 9),
 
 ('sys_manage_approve', 'content_id', 2147483647, 1, 1),
 ('sys_manage_approve', 'notes', 2147483647, 1, 2),

+ 2 - 0
modules/boonex/english/data/langs/system/en.xml

@@ -2539,6 +2539,7 @@ If it is not enabled then please consider implement this optimization, since it
 	<string name="_sys_form_wiki_input_caption_notes"><![CDATA[Notes]]></string>
 	<string name="_sys_form_wiki_input_caption_submit"><![CDATA[Submit]]></string>
 	<string name="_sys_form_wiki_input_caption_close"><![CDATA[Close]]></string>
+	<string name="_sys_form_wiki_input_caption_files"><![CDATA[Images]]></string>
 	<string name="_sys_form_wiki_buttons"><![CDATA[Buttons]]></string>
 	<string name="_sys_format_size_b"><![CDATA[{0} bytes]]></string>
 	<string name="_sys_format_size_gb"><![CDATA[{0} GB]]></string>
@@ -3536,6 +3537,7 @@ If it is not enabled then please consider implement this optimization, since it
     <string name="_sys_wiki_title_content"><![CDATA[{0} content]]></string>
     <string name="_sys_wiki_title_time"><![CDATA[{0} updated]]></string>
     <string name="_sys_wiki_orig"><![CDATA[Orig.]]></string>
+    <string name="_sys_wiki_copy"><![CDATA[Copy to clipboard]]></string>
 
 	<string name="_adm_lmi_cpt_groups_roles"><![CDATA[Roles]]></string>
 	<string name="_adm_form_txt_groups_roles_add_popup"><![CDATA[Create role]]></string>

+ 3 - 0
modules/boonex/russian/data/langs/system/ru.xml

@@ -2524,6 +2524,8 @@
     <string name="_sys_form_wiki_input_caption_content_main"><![CDATA[Основное(оригинальное) содержимое]]></string>
 	<string name="_sys_form_wiki_input_caption_notes"><![CDATA[Заметки]]></string>
 	<string name="_sys_form_wiki_input_caption_submit"><![CDATA[Сохранить]]></string>
+	<string name="_sys_form_wiki_input_caption_close"><![CDATA[Закрыть]]></string>
+	<string name="_sys_form_wiki_input_caption_files"><![CDATA[Изображения]]></string>
 	<string name="_sys_format_size_b"><![CDATA[{0} байт]]></string>
 	<string name="_sys_format_size_gb"><![CDATA[{0} ГБ]]></string>
 	<string name="_sys_format_size_kb"><![CDATA[{0} КБ]]></string>
@@ -3500,6 +3502,7 @@
     <string name="_sys_wiki_title_content"><![CDATA[{0} содержимое]]></string>
     <string name="_sys_wiki_title_time"><![CDATA[{0} обновлен]]></string>
     <string name="_sys_wiki_orig"><![CDATA[Ориг.]]></string>
+    <string name="_sys_wiki_copy"><![CDATA[Копировать в буфер обмена]]></string>
 
 	<string name="_adm_lmi_cpt_groups_roles"><![CDATA[Роли]]></string>
 	<string name="_adm_form_txt_groups_roles_add_popup"><![CDATA[Создать роль]]></string>

+ 11 - 0
template/css/wiki.css

@@ -64,3 +64,14 @@
     -ms-transition: background-color 1500ms linear;
     transition: background-color 1500ms linear;
 }
+
+.wiki-uploader-file {
+    display: flex;
+    flex-direction: row;
+    justify-content: flex-start;
+    align-items: flex-start;
+}
+.wiki-uploader-file-preview > img {
+    height:3.3rem;
+    width:auto;
+}

+ 32 - 1
template/scripts/BxBaseFormWiki.php

@@ -18,8 +18,39 @@ class BxBaseFormWiki extends BxTemplFormView
 
         if (isset($this->aInputs['language']))
             $this->aInputs['language']['values'] = BxDolLanguages::getInstance()->getLanguages(false, true);
-        if (isset($this->aInputs['content']))
+        if (isset($this->aInputs['content'])) {
+            $this->aInputs['content']['attrs']['id'] = 'wiki_content_';
             $this->aInputs['content']['code'] = true;
+        }
+
+    	if(isset($this->aInputs['files'])) {
+            $this->aInputs['files']['storage_object'] = 'sys_wiki_files';
+            $this->aInputs['files']['uploaders'] = !empty($this->aInputs['files']['value']) ? unserialize($this->aInputs['files']['value']) : array('sys_html5');
+            $this->aInputs['files']['storage_private'] = 0;
+            $this->aInputs['files']['multiple'] = true;
+            $this->aInputs['files']['content_id'] = 0;
+            $this->aInputs['files']['ghost_template'] = '';
+        }
+    }
+
+    function initChecker ($aValues = array (), $aSpecificValues = array())
+    {
+        parent::initChecker ($aValues, $aSpecificValues);
+
+        if (isset($this->aInputs['files'])) {
+            if (!empty($this->aInputs['block_id']) && !empty($this->aInputs['block_id']['value'])) {
+                $this->aInputs['files']['content_id'] = $this->aInputs['block_id']['value'];
+                $sWikiEditorId = 'wiki_content_' . $this->aInputs['files']['content_id'];
+                $this->aInputs['files']['ghost_template'] = $this->oTemplate->parseHtmlByName('wiki_uploader_file.html', array (
+                    'name' => $this->aInputs['files']['name'],
+                    'content_id' => (int)$this->aInputs['files']['content_id'],
+                    'editor_id' => $sWikiEditorId,
+                ));
+
+                if (isset($this->aInputs['content']))
+                    $this->aInputs['content']['attrs']['id'] = $sWikiEditorId;
+            }
+        }
     }
 
     function genLabel(&$aInput)

+ 14 - 0
template/wiki_uploader_file.html

@@ -0,0 +1,14 @@
+<div id="bx-uploader-file-{storage_object}-{file_id}" class="bx-uploader-ghost wiki-uploader-file bx-def-margin-sec-top">
+    <input type="hidden" name="__name__[]" value="{file_id}" />
+    <div class="wiki-uploader-file-preview bx-def-round-corners bx-def-margin-sec-right">
+        <img src="{file_icon}" class="bx-def-round-corners bx-base-general-icon bx-base-general-icon-{file_id}" />
+    </div>
+    <div class="wiki-uploader-file-title-actions">
+        <div class="wiki-uploader-file-title">{file_title}</div>
+        <div class="wiki-uploader-file-actions bx-def-margin-thd-top bx-clearfix">
+            <a class="bx-btn bx-btn-small bx-def-margin-sec-right" href="javascript:void(0);" onclick="{js_instance_name}.deleteGhost('{file_id}'); bx_wiki_remove_img('#__editor_id__', '{storage_object}/{file_remote_id}')" title="<bx_text_attribute:_Delete />"><i class="sys-icon remove"></i></a>
+            <a class="bx-btn bx-btn-small bx-def-margin-sec-right" href="javascript:void(0);" onclick="var _e = this; bx_copy_to_clipboard('{file_url}', function (){ $(_e).find('.sys-icon').removeClass('copy').addClass('check')})" title="<bx_text_attribute:_sys_wiki_copy />"><i class="sys-icon copy"></i></a>
+            <a class="bx-btn bx-btn-small bx-def-margin-sec-right" href="javascript:void(0);" onclick="bx_wiki_insert_img ('#__editor_id__', '{storage_object}/{file_remote_id}', '{file_title}')" title="<bx_text_attribute:_sys_txt_form_entry_input_picture_insert />"><i class="sys-icon file-export"></i></a>
+        </div>
+    </div>
+</div>