CMSiMDE AJAX 存檔

CMSiMDE 是在 2013 年底啟動開發, 當初只是希望將 CherryPy 換成 Flask. 動機是 Flask 相對簡潔, 而且正在崛起.

Flask

假如您曾經體驗過 Zope, 尤其是 Zope 2.0 時代的所謂崛起, 在 Google 尚未成氣候之際, PHP 仍然陽春, 雲端虛擬主機技術還不是很穩定, Zope 當時如日中天, 但是當各種小型網際框架不斷崛起, 雲端免費主機一一出現之後, Zope 即便以 3.0 應戰, 仍然逐漸消失......

因此儘管 CherryPy 也是非常優秀的 Python 網際程式框架, 但是當 Flask 崛起, 用戶逐漸增多之後, 在 2013 年底就決定將原先使用的 CherryPy 換為 Flask, 並且使用 CMSiMDE 取代先前的 CMSimplyCMSimfly.

TinyMCE

最早的 CMSimply 使用 TinyMCE 3.0 版本的網際編輯器, 當 2016 年 TinyMCE 改版為 4.0 之後, CMSimply 也同時改版. 今年 3 月也將 CMSiMDE 改為 IPv6 相容, 過程中也對 TinyMCE 4.0 進行部分客制化改版, 因此現在即使最新的 TinyMCE 已經轉進 5.0 版, 但是 CMSiMDE 目前仍然決定自行維護 4.0 版以及所有使用中的 Plugins.

TinyMCE 4.9.11 相關的所有原始碼可以在這裡找到.

csave

由於 CMSiMDE 採用 html 編輯器, 而且採單一管理者流程設計, 在 KMOLab 課程中多人協同維護使用, 有一定難度, 但是源自 CMSimple-XH 的 H1~H3 標註分頁的特性, 讓目前的 CMSiMDE 仍然必須使用 html 編輯器.

為了因應在開放網路中多人同步編輯 CMSiMDE 的環境下, 曾經試著透過 csave 按鈕解決. csave 的編輯概念是, 假如在開放網路中, 多人幾乎同時登入 CMSiMDE 對同一個頁面進行編輯, 各自按下 csave 按鈕時, 系統會將目前編輯的版本與已經存在 config/content.htm 中的分頁進行比對, 然後同時儲存兩個版本中的所有內容.

由於 csave 採用 Beautifulsoup 進行標註分類, 將編輯中與已經存檔的頁面中的所有節點, 按照合併後的數列進行順序排列, 因此所得到的結果可能不是協同者期望中的頁面內容, 但基本構想是至少不會因為多人同步編輯下, 丟失某一協同者的改版內容.

當然, 目前的 csave 尚未進行長期大量測試, 內容合併功能可能仍有許多問題, 但是已經是應用開放網路同步編輯流程中沒有辦法中的辦法:-)

AJAX

我們知道 AJAX 是目前網際程式能夠與單機程式執行流程相似的一項技術, 當使用者在瀏覽器中對遠端的伺服器功能進行選擇處理時, 資料的交換能夠畫面更新進行切割, 使用者點選特定功能鍵, 主體畫面仍能維持不變.

CMSiMDE 目前所使用的各項功能, 只有 upload 檔案採用 AJAX 技術, 而 TinyMCE 編輯器中的 Edit All 或 Edit 功能, 目前在存檔後, 都必須 reload 畫面.

為了讓 CMSiMDE 的編輯流程更加順暢, 當使用者在編輯單一頁面內容時, 或許也可以採用 AJAX 存檔.

在思考進行改版之前, 第一個碰到的問題是, CMSiMDE 的分頁存取, 所依賴的 pivot, 是 H1, H2 與 H3 標題, 將 CMSiMDE 改為 AJAX, 就技術上或許沒有問題, 但是必須考慮當使用者在頁面中新增多個其他分頁標題, 以及使用者因為需求, 刪除當前頁面的標題, 或者將頁面中的分頁 H1, H2, H3 改為上一頁面的 H4 標註等情況.

此外, 當 TinyMCE editor 啟動之後, sh4tinymce plugin 會有機會出入編輯器, 先前當使用者對既有的 sh4tinymce (類似 code sample plugin 的程式碼 highlighter 延伸程式) 進行改版時, editor 中的 save 延伸程式並無法感知的問題, 也必須一併處理.

sh4tinymce bug

長久以來 sh4tinymce 一直存在一個 bug, 當使用者在 editor 對既有的程式碼引用 pre 區域改版後 (首次改版沒有問題), editor 中的 Save 並無法感知, 使用者必須使用下方的 html save input 按鈕才能正確將引用程式內容存檔.

造成此一 bug 的問題出在 sh4tinymce 126 行, 當使用者按下 sh4tinymce 中的 OK 按鈕, onSubmitFunction 並沒有知會 TinyMCE editor, 因此在同一頁面中只要多次改版, editor 中的 Save 按鈕並無法點擊, 必須以下方的 Save 才能存檔.

下方的修改, 只需要在離開 onSubmotFunction 之前, 透過 tinymce.activeEditor.setDirty(true); 知會 editor, 內容已經更動即可:

function onSubmitFunction(e) {
    var code = e.data.code;
    code = code.replace(/\/g,">");
    /* Convert settings into strings for classname */
    var language    = e.data.language ? e.data.language : defaultLanguage;
    var collapse    = e.data.collapse != shDefault.collapse ? ';collapse:' + e.data.collapse : '';
    var autolinks   = e.data.autolinks != shDefault.autolinks ? ';auto-links:' + e.data.autolinks : '';
    var gutter      = e.data.gutter != shDefault.gutter ? ';gutter:' + e.data.gutter : '';
    var htmlscript  = e.data.htmlscript != shDefault.htmlscript ? ';html-script:' + e.data.htmlscript : '';
    var toolbar     = e.data.toolbar != shDefault.toolbar ? ';toolbar:' + e.data.toolbar : '';
    var firstline   = e.data.firstline != shDefault.firstline ? ';first-line:' + e.data.firstline : '';
    var hlstart=e.data.highlight.indexOf(",")!=-1?"[":"",
        hlend=e.data.highlight.indexOf(",")!=-1?"]":"";
    var highlight   = e.data.highlight.replace(/ /g,"").replace(/\[/g,"").replace(/\]/g,"") != shDefault.highlight ? ';highlight:' + hlstart + e.data.highlight.replace(/ /g,"").replace(/\[/g,"").replace(/\]/g,"").replace(/,$/g,"") + hlend : '';
    var tabsize     = e.data.tabsize != shDefault.tabsize ? ';tab-size:' + e.data.tabsize : '';

    // Create SH element with string settings
    Elmt = editor.dom.create('pre',
                {class: 'brush:' + language + collapse + autolinks + gutter + htmlscript + toolbar + firstline + highlight + tabsize,
                 contenteditable: 'false'},
                 code);

    if(selected)
        editor.dom.replace(Elmt, selectionNode);
    else
        editor.insertContent(editor.dom.getOuterHTML(Elmt)+'
'); tinymce.activeEditor.setDirty(true); }

editor

接下來為了讓 TinyMCE 可以使用 AJAX 存檔, 還必須修改三個地方, 其中兩項修改是針對 savePage 與 ssavePage, 都是蓋掉原先傳統存檔流程處理跳行的 page_content = page_content.replace("\n",""), 在利用 TinyMCE Javascript 存檔時必須蓋掉.

而最後一項修改則是針對 tinymce_editor:

def tinymce_editor(menu_input=None, editor_content=None, page_order=None):

    """Tinymce editor scripts
    """

    sitecontent =file_get_contents(config_dir + "content.htm")
    editor = set_admin_css() + editorhead() + '''''' + editorfoot()
    # edit all pages
    if page_order is None:
        outstring = editor + "<div class='container'>
\ " outstring +=""" """ outstring += "
" else: # add viewpage button while single page editing head, level, page = parse_content() outstring = "

" outstring += editor + "<div class='container'>
\ " # add an extra collaborative save button outstring += """""" outstring += """""" outstring +=""" """ outstring += '''
''' return outstring

上述修改必須要測試一段期間後, 才會從 CMSDEBUG 併入 CMSiMDE 改版.

FastAPIASGI

CMSiMDE 中的 Flask 才剛剛改為 AJAX, 也能夠採 WSGI 架構配置在 Ubuntu 伺服器, 但另外一波的技術又已經逐漸崛起. 那就是 FastAPIASGI.

CMSiMDE 開發屆滿十年之前, 眼看著內部核心技術又要跟著改版了. 長江後浪推前浪, 網際舊框架不死, 只是越來越少人使用......