Sharing TinyMCE plugin across multiple application

2019-08-12 03:00发布

问题:

I'm using CakePHP 2.4.7 and the TinyMCE plugin from CakeDC.

I set up my CakePHP core along with the plugin in a shared location on my server so that multiple applications can access it. This keeps me from having to update multiple copies of TinyMCE. Everything was working well until I migrated to a new server and updated software.

The new server is running Apache 2.4 instead of 2.2 and using mod_ruid2 instead of suexec.

I now get this error when trying to load the editor:

Fatal Error (4): syntax error, unexpected T_CONSTANT_ENCAPSED_STRING in [/xyz/Plugin/TinyMCE/webroot/js/tiny_mce/tiny_mce.js, line 1]

How should I start debugging this?

Workaround Attempt

I tried adding a symlink from an application's webroot to TinyMCE's plugin webroot. This works in that it loads the js file and the editor, but then TinyMCE plugins are working on the wrong current directory and file management would not be separated.

回答1:

The problem is the AssetDispatcher filter, it includes css and js files using PHPs include() statement, causing the files to be sent through the PHP parser, where it will stumble over the occurrences of <? in the TinyMCE script.

See https://github.com/.../2.4.7/lib/Cake/Routing/Filter/AssetDispatcher.php#L159-L160

A very annoying, and, since it's undocumented and non-optional, dangerous behavior if you ask me.

Custom asset dispatcher

In case you want to continue to use a plugin asset dispatcher, extend the built in one, and reimplement the AssetDispatcher::_deliverAsset() method with the include functionality removed. Of course this is kinda annoying, maintenance wise, but it's a pretty quick fix.

Something like:

// app/Routing/Filter/MyAssetDispatcher.php

App::uses('AssetDispatcher', 'Routing/Filter');

class MyAssetDispatcher extends AssetDispatcher {
    protected function _deliverAsset(CakeResponse $response, $assetFile, $ext) {
        // see the source of your CakePHP core for the
        // actual code that you'd need to reimpelment

        ob_start();
        $compressionEnabled = Configure::read('Asset.compress') && $response->compress();
        if ($response->type($ext) == $ext) {
            $contentType = 'application/octet-stream';
            $agent = env('HTTP_USER_AGENT');
            if (preg_match('%Opera(/| )([0-9].[0-9]{1,2})%', $agent) || preg_match('/MSIE ([0-9].[0-9]{1,2})/', $agent)) {
                $contentType = 'application/octetstream';
            }
            $response->type($contentType);
        }
        if (!$compressionEnabled) {
            $response->header('Content-Length', filesize($assetFile));
        }
        $response->cache(filemtime($assetFile));
        $response->send();
        ob_clean();


        // instead of the possible `include()` in the original
        // methods source, use `readfile()` only 
        readfile($assetFile);


        if ($compressionEnabled) {
            ob_end_flush();
        }
    }
}
// app/Config/bootstrap.php

Configure::write('Dispatcher.filters', array(
    'MyAssetDispatcher', // instead of AssetDispatcher
    // ...
));

See also http://book.cakephp.org/2.0/en/development/dispatch-filters.html

Don't just disable short open tags

I'm just guessig here, but the reason why it was working on your other server probably is that short open tags (ie <?) where disabled. However even if that is the problem on your new server, this isn't something you should rely on, the assets are still being served using include(), and you most probably don't want to check all your third party CSS/JS for possible PHP code injections on every update.