외부 이미지 복붙 -> 바로 파일 첨부에 관해 고민을 하고 있는데요.

애드온보다는 코어 위시 리스트에 있는 기능이라는 언급이 있어서

일단 (감히 코어를 건드릴 깜냥은 못돼서ㅜ) 애드온으로 디벨롭해보고 있습니다.

특히 @기진곰님의 이미지 자동 첨부 애드온의 소스를 활용했습니다 :)

 

현재 다음과 같은 구조인데요.

사실 procFileUpload인가 하는 함수를 exec_json으로 처리하면 간단할 것 같은데, 이게 외부이미지 파일 첨부 때문에 쉽지가 않더라구요. 그래서 복잡ㅜ

암튼 현재까지 파일 첨부는 잘 되고 있습니다 :D

  1. CK에디터가 로드됐을 때, paste 이벤트를 걸어줌.
  2. paste된 내용 중에 이미지 태그가 있으면 ajax로 서버와 통신.
  3. 서버단에서는 캐시를 이용해 전달된 문서/댓글 srl과 이미지 url을 기준으로 선별적으로 파일 첨부(insertFile 함수 실행)를 진행.
  4. 서버에서 다시 스크립트쪽으로 img src에 들어갈 첨부파일 경로를 전달
  5. 스크립트에서는 1) 에디터의 첨부파일 리스트를 리로드하고, 2) paste 내용에서 기존 img src를 서버쪽 첨부파일의 src로 바꿔치기

 

아직 정리는 덜 됐지만ㅎ 현재 작업한 소스는 대강 다음과 같습니다.

 

현재까지 제가 발견한 문제는 이렇습니다.

1) 일단 img 태그 식별할 때 외부이미지인지, 서버쪽 이미지인지 분간이 안 되고 있네요ㅜ

2) 이미지 자동 첨부 애드온에 있는 gif 처리 방식도 있으면 좋을 것 같고

3) 이미지 복붙으로 파일을 첨부하는 동안 로딩 이미지도 있으면 좋겠구요.

고려사항이 더 있거나 하면 알려주시면 감사하겠습니다~

 

jQuery(document).ready(function($) {

    var editor = $('[data-editor-primary-key-name$="_srl"]');
    if ( editor.length < 1 ) {
        return;
    }
    var paste = '';
    var matches = [];

    var editor_sequence = editor.data('editor-sequence');
    var editor_target = editor.data('editor-primary-key-name');
    var upload_target_srl = $('[name="'+ editor_target +'"]').val();
    var img_url_list = [], img_src_list = [];
    var is_image = /<img [^>]*src="[^"]*"[^>]*>/gm;

    setTimeout(function() {
        var ck_editor = CKEDITOR.instances.editor1;

        ck_editor.on('paste', function(e) {
            paste = e.data.dataValue;
            matches = paste.match(is_image);
            if ( matches ) {
                img_url_list = matches.map(x => x.replace(/.*src="([^"]*)".*/, '$1'));
                var params = {
                    mid: current_mid,
                    image_urls: img_url_list.join(','),
                    editor_sequence: editor_sequence,
                    upload_target_srl: upload_target_srl
                }
                $.ajax({
                    url: 서버쪽 php 파일,
                    dataType: 'json',
                    data: params,
                    async: false,
                    success: function(data) {
                        $container.data('instance').loadFilelist($container);
                        img_src_list = data;
                    },
                    complete: function() {
                        $.map(img_url_list, function(v, i) {
                            paste = paste.replace(v, img_src_list[i]);
                        });
                    },
                    error: function(x,e) {
                        if ( x.status == 0 ) {
                            alert('네트워크 연결 상태를 체크해주세요.');
                        } else if ( x.status == 404 ) {
                            alert('요청받은 URL을 찾을 수 없습니다.');
                        } else if ( x.status == 500 ) {
                            alert('내부 서버 오류 : 관리자에게 문의해보세요.');
                        } else if ( e == 'parsererror' ) {
                            alert('요청받은 내용을 변환하는 실패했습니다.');
                        } else if ( e == 'timeout' ) {
                            alert('연결 시간이 초과했습니다.');
                        } else {
                            alert('알 수 없는 에러가 발생했습니다.\n' + x.responseText);
                        }
                    }
                });
                e.data.dataValue = paste;
            } else {
                return false;
            }
        });
    }, 1200);

});

 

<?php

/**
 * @Most Parts of this code below are brought from
 * @https://github.com/poesis/xe-autoattach/blob/master/autoattach.class.php
 * @author Kijin Sung <[email protected]>
 * @license GPLv2 or Later <https://www.gnu.org/licenses/gpl-2.0.html>
 *
 * @The Original source is described like this :
 * This addon automatically finds unattached images in documents and comments
 * and converts them into real attachments. This can be useful because
 * many users cannot distinguish between external images and real attachments,
 * but the website administrator must be careful because self-hosting all
 * images may result in copyright infringement.
 */

include '../../common/autoload.php';
Context::init();

$image_url_list = explode(',', $_REQUEST['image_urls']);
$editor_sequence = (int)$_REQUEST['editor_sequence'];
// Get upload_target_srl
$upload_target_srl = (int)$_REQUEST['upload_target_srl'] ?: 0;
if (!$upload_target_srl)
{
    $upload_target_srl = $_SESSION['upload_info'][$editor_sequence]->upload_target_srl;
}
if (!$upload_target_srl)
{
    $upload_target_srl = getNextSequence();
    $_SESSION['upload_info'][$editor_sequence]->upload_target_srl = $upload_target_srl;
}

$logged_info = Context::get('logged_info');
$module_srl = ModuleModel::getModuleInfoByMid($_REQUEST['mid'])->module_srl;
$module_config = FileModel::getFileConfig($module_srl);

$file_info = array();
$response = array();

foreach ( $image_url_list as $key => $image_url )
{
    // If the same image has already been downloaded, reuse the cached version.
    $cache_key = 'url_cache:' . getNumberingPath($upload_target_srl) . $image_url;
    if ( Rhymix\Framework\Cache::get($cache_key) )
    {
        $uploaded_filename = Rhymix\Framework\Cache::get($cache_key);
        $response[$key] = str_replace('/addons/youtube_converter', '', htmlspecialchars($uploaded_filename));
        continue;
    }

    // Attempt to download the image.
    $temp_path = _XE_PATH_ . 'files/cache/youtube_converter/' . md5($image_url . microtime() . mt_rand());
    $download_start_time = microtime(true);
    $image_timeout = 4;
    $redirect_settings = array('follow_redirects' => true, 'max_redirects' => 2);
    $status = FileHandler::getRemoteFile(urldecode($image_url), $temp_path, null, $image_timeout, 'GET', null, array(), array(), array(), $redirect_settings);
    clearstatcache($temp_path);
    if (!$status || !file_exists($temp_path) || !filesize($temp_path))
    {
        FileHandler::removeFile($temp_path);
        continue;
    }

    // Check the current module's attachment size limit.
    if ($module_config->allowed_filesize && ($_SERVER['REQUEST_METHOD'] === 'GET' || $logged_info->is_admin !== 'Y'))
    {
        if (filesize($temp_path) > $module_config->allowed_filesize * 1024 * 1024)
        {
            FileHandler::removeFile($temp_path);
            continue;
        }
    }
    if ($module_config->allowed_attach_size && ($_SERVER['REQUEST_METHOD'] === 'GET' || $logged_info->is_admin !== 'Y'))
    {
        $total_size = executeQuery('file.getAttachedFileSize', (object)array('upload_target_srl' => $upload_target_srl));
        if($total_size->data->attached_size + filesize($temp_path) > $module_config->allowed_attach_size * 1024 * 1024)
        {
            FileHandler::removeFile($temp_path);
            continue;
        }
    }

    // Guess the correct filename and extension.
    if (preg_match('@[^\\\\/\\?=]+\.(gif|jpe?g|png|bmp|svg)\b@i', urldecode($image_url), $matches))
    {
        $temp_name = $matches[0];
    }
    else
    {
        $temp_name = md5($image_url . microtime() . mt_rand());
    }
    if (preg_match('/^[0-9a-f]{32}$/', $temp_name))
    {
        $image_info = @getimagesize($temp_path);
        if (!$image_info) $temp_ext = 'jpg';

        switch ($image_info['mime'])
        {
            case 'image/gif': $temp_ext = 'gif';
            case 'image/jpeg': $temp_ext = 'jpg';
            case 'image/png': $temp_ext = 'png';
            case 'image/x-ms-bmp': $temp_ext = 'bmp';
            default: $temp_ext = 'jpg';
        }
        $temp_name .= '.' . $temp_ext;
    }

    $file_info['name'] = $temp_name;
    $file_info['tmp_name'] = $temp_path;

    // Register as attachment.
    $oFile = getController('file')->insertFile($file_info, $module_srl, $upload_target_srl, 0, true);
    FileHandler::removeFile($temp_path);
    if (!$oFile)
    {
        continue;
    }
    Rhymix\Framework\Cache::set($cache_key, $oFile->get('uploaded_filename'), 3600 * 24 * 30);

    // Create the response
    if ($oFile->get('direct_download') === 'Y')
    {
        $response[$key] = str_replace('/addons/youtube_converter', '', FileModel::getDirectFileUrl($oFile->get('uploaded_filename')));
    }
    else
    {
        $response[$key] = str_replace('/addons/youtube_converter', '', FileModel::getDownloadUrl($oFile->get('file_srl'), $oFile->get('sid'), $module_srl));
    }
}

echo json_encode($response);

Context::close();

?>

 

 

  • profile

    에디터자동완성모듈 에서도
    1)내.외부 이미지 구분 없이 작동 합니다.

    -클립보드에 순수한 이미지정보만 있지 않을까요?


    3)첨부가 워낙 순식간이어서 에디터 하단에 첨부 부분에 첨부된게 보여서 어색하거나 첨부가 되는지 모르지는 않는거 같습니다.

  • profile profile
    1) 클립보드에는 img 태그의 src 내용이 포함되기 때문에 파일 이름뿐 아니라 파일 경로까지 포함되게 되어 있어요. 그래서 조금만 더 신경쓰면 정규식 처리로 복붙 이미지의 내외부 식별은 어렵지 않게 할 수 있겠는데, 자동링크 애드온인가, 외부 링크에 target 달아주는 애드온 있지 않습니까? 거기서 좀 참고를 해보면 어떨까 생각 중입니다..

    3) 첨부 자체는 자동적으로 이뤄지긴 하는데, 복붙 과정에서 다양한 처리들이 이뤄지다보니 약간 딜레이가 있긴 합니다. 그래서 프로그레스 이미지나 로딩 이미지를 보여주면 좋을 것 같더라구요. 사이트 이용자들 당황하지 마시라는 뜻에서요ㅎㅎ
  • profile
    아, 그리고 여기서 테스트해볼 수 있습니다.
    https://dev.aporia.blog/board_fKje44
  • profile

    생각해보니

    1) 일단 img 태그 식별할 때 외부이미지인지, 서버쪽 이미지인지 분간이 안 되고 있네요ㅜ

    2) 이미지 자동 첨부 애드온에 있는 gif 처리 방식도 있으면 좋을 것 같고

    3) 이미지 복붙으로 파일을 첨부하는 동안 로딩 이미지도 있으면 좋겠구요.

    4) php 파일에서 접근권한 확인을 해야겠군요. 쓰기 권한이라든가, 파일 첨부 권한이라든가;;;

    5) 첨부 파일을 삭제했을 경우에는 캐시도 삭제해줘야 하겠구요;;;