├── .gitignore ├── README.md ├── _config.yml ├── composer.json └── src ├── FroalaEditorAsset.php └── FroalaEditorWidget.php /.gitignore: -------------------------------------------------------------------------------- 1 | # phpstorm project files 2 | .idea 3 | *.iml 4 | 5 | # netbeans project files 6 | nbproject 7 | 8 | # zend studio for eclipse project files 9 | .buildpath 10 | .project 11 | .settings 12 | 13 | # windows thumbnail cache 14 | Thumbs.db 15 | 16 | # composer vendor dir 17 | /vendor 18 | 19 | # composer itself is not needed and related files 20 | composer.phar 21 | composer.lock 22 | 23 | # Mac DS_Store Files 24 | .DS_Store 25 | 26 | # phpunit itself is not needed 27 | phpunit.phar 28 | # local phpunit config 29 | /phpunit.xml 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Yii Framework Froala WYSIWYG HTML Editor 2 | 3 | [![Packagist](https://img.shields.io/packagist/v/froala/yii2-froala-editor.svg)](https://packagist.org/packages/froala/yii2-froala-editor) 4 | [![Packagist](https://img.shields.io/packagist/dt/froala/yii2-froala-editor.svg)](https://packagist.org/packages/froala/yii2-froala-editor) 5 | 6 | >Yii 2 widget for Froala Wysiwyg editor. 7 | 8 | ## Installation 9 | 10 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 11 | 12 | Either run 13 | 14 | ``` 15 | php composer.phar require --prefer-dist froala/yii2-froala-editor 16 | ``` 17 | 18 | or add 19 | 20 | ``` 21 | "froala/yii2-froala-editor": "^3.2.5" 22 | ``` 23 | 24 | to the require section of your `composer.json` file. 25 | 26 | 27 | ## Usage 28 | 29 | Once the extension is installed, simply use it in your code by : 30 | 31 | ```php 32 | 'content', 34 | 'options' => [ 35 | // html attributes 36 | 'id'=>'content' 37 | ], 38 | 'clientOptions' => [ 39 | 'toolbarInline'=> false, 40 | 'theme' =>'royal', //optional: dark, red, gray, royal 41 | 'language'=>'en_gb' // optional: ar, bs, cs, da, de, en_ca, en_gb, en_us ... 42 | ] 43 | ]); ?> 44 | ``` 45 | 46 | or use with a model: 47 | 48 | ```php 49 | $model, 51 | 'attribute' => 'content', 52 | 'options' => [ 53 | // html attributes 54 | 'id'=>'content' 55 | ], 56 | 'clientOptions' => [ 57 | 'toolbarInline' => false, 58 | 'theme' => 'royal', //optional: dark, red, gray, royal 59 | 'language' => 'en_gb' // optional: ar, bs, cs, da, de, en_ca, en_gb, en_us ... 60 | ] 61 | ]); ?> 62 | ``` 63 | 64 | add Font-awesome cdn for font-awesome plugin 65 | ```php 66 | 67 | ``` 68 | 69 | ## Upload example 70 | 71 | Using the basic Yii template make a new folder under /web/ called uploads. 72 | 73 | For controler: 74 | 75 | ```php 76 | public function actionUpload() { 77 | $base_path = Yii::getAlias('@app'); 78 | $web_path = Yii::getAlias('@web'); 79 | $model = new UploadForm(); 80 | 81 | if (Yii::$app->request->isPost) { 82 | $model->file = UploadedFile::getInstanceByName('file'); 83 | 84 | if ($model->validate()) { 85 | $model->file->saveAs($base_path . '/web/uploads/' . $model->file->baseName . '.' . $model->file->extension); 86 | } 87 | } 88 | 89 | // Get file link 90 | $res = [ 91 | 'link' => $web_path . '/uploads/' . $model->file->baseName . '.' . $model->file->extension, 92 | ]; 93 | 94 | // Response data 95 | Yii::$app->response->format = Yii::$app->response->format = Response::FORMAT_JSON; 96 | return $res; 97 | } 98 | ``` 99 | 100 | For model: 101 | 102 | ```php 103 | namespace app\models; 104 | use yii\base\Model; 105 | use yii\web\UploadedFile; 106 | 107 | /** 108 | * UploadForm is the model behind the upload form. 109 | */ 110 | class UploadForm extends Model 111 | { 112 | /** 113 | * @var UploadedFile|Null file attribute 114 | */ 115 | public $file; 116 | 117 | /** 118 | * @return array the validation rules. 119 | */ 120 | public function rules() 121 | { 122 | return [ 123 | [['file'], 'file'] 124 | ]; 125 | } 126 | } 127 | ``` 128 | 129 | For the view: 130 | 131 | ```php 132 | 'body', 134 | 'clientOptions' => [ 135 | 'toolbarInline'=> false, 136 | 'height' => 200, 137 | 'theme' => 'royal',//optional: dark, red, gray, royal 138 | 'language' => 'en_gb' , 139 | 'toolbarButtons' => ['fullscreen', 'bold', 'italic', 'underline', '|', 'paragraphFormat', 'insertImage'], 140 | 'imageUploadParam' => 'file', 141 | 'imageUploadURL' => \yii\helpers\Url::to(['site/upload/']) 142 | ], 143 | 'clientPlugins'=> ['fullscreen', 'paragraph_format', 'image'] 144 | ]); ?> 145 | ``` 146 | 147 | For full details on usage, see the [documentation](https://froala.com/wysiwyg-editor/docs). 148 | 149 | ## Custom Buttons Example 150 | 151 | The custom Buttons can be defined in JS files anywhere you want, in this example in /basic/assets/ folder. 152 | 153 | In the view: 154 | 155 | ```php 156 | registerJsFile('/basic/assets/alert.js', ['depends' => '\Froala\Editor\FroalaEditorAsset']);?> 157 | 158 | 'body', 160 | 'clientOptions' => [ 161 | 'toolbarInline' => false, 162 | 'height' => 200, 163 | 'theme' => 'royal',//optional: dark, red, gray, royal 164 | 'language' => 'en_gb', 165 | 'toolbarButtons' => ['fullscreen', 'bold', 'italic', 'underline', '|', 'paragraphFormat', 'insertImage', 'alert'] 166 | ], 167 | 'clientPlugins' => ['fullscreen', 'paragraph_format', 'image'] 168 | ]); ?> 169 | ``` 170 | 171 | In /basic/assets/alert.js: 172 | 173 | ```js 174 | FroalaEditor.DefineIcon('alert', {NAME: 'info'}); 175 | FroalaEditor.RegisterCommand('alert', { 176 | title: 'Hello', 177 | focus: false, 178 | undo: false, 179 | refreshAfterCallback: false, 180 | callback: function () { 181 | alert('Hello!'); 182 | } 183 | } 184 | ); 185 | ``` 186 | 187 | For more details you can go to [Custom Buttons](https://www.froala.com/wysiwyg-editor/examples/custom-buttons) 188 | ## License 189 | 190 | This package is available under MIT License. However, Froala editor requires purchasing a license from https://www.froala.com/wysiwyg-editor/pricing. 191 | -------------------------------------------------------------------------------- /_config.yml: -------------------------------------------------------------------------------- 1 | theme: jekyll-theme-minimal -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "froala/yii2-froala-editor", 3 | "version": "4.5.2", 4 | "description": "A beautiful WYSIWYG HTML text editor based on HTML5 technology. Cross browser, with mobile support, high performance and Retina Ready modern design.", 5 | "type": "yii2-extension", 6 | "keywords": [ 7 | "froala", 8 | "html", 9 | "text", 10 | "editor", 11 | "wysiwyg", 12 | "rich editor", 13 | "rich text editor", 14 | "rte", 15 | "javascript" 16 | ], 17 | "license": "MIT", 18 | "authors": [ 19 | { 20 | "name": "dungphanxuan", 21 | "email": "dungphanxuan999@gmail.com" 22 | } 23 | ], 24 | "require": { 25 | "yiisoft/yii2": "^2.0", 26 | "froala/wysiwyg-editor": "4.5.2", 27 | "rmrevin/yii2-fontawesome": "^2.0 | ^3.0" 28 | }, 29 | "autoload": { 30 | "psr-4": { 31 | "froala\\froalaeditor\\": "src/" 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/FroalaEditorAsset.php: -------------------------------------------------------------------------------- 1 | registerPlugins($clientPlugins); 71 | $pluginNames = array_values($clientPlugins); 72 | } else { 73 | // associative array = custom plugins and options included 74 | foreach ($clientPlugins as $key => $value) { 75 | if (is_numeric($key)) { 76 | $pluginName = $value; 77 | if (!$this->isPluginExcluded($pluginName, $excludedPlugins)) { 78 | $this->registerPlugin($pluginName); 79 | } 80 | } else { 81 | $pluginName = $key; 82 | if (!$this->isPluginExcluded($pluginName, $excludedPlugins)) { 83 | $pluginOptions = $value; 84 | $issetJs = isset($pluginOptions['js']); 85 | $issetCss = isset($pluginOptions['css']); 86 | if ($issetJs) { 87 | $this->addJs($pluginOptions['js']); 88 | } else { 89 | if ($this->isPluginJsFileExist($pluginName)) { 90 | $this->addJs($this->getDefaultJsUrl($pluginName)); 91 | } else { 92 | throw new Exception("you must set 'js' [and 'css'] for plugin '$pluginName'"); 93 | } 94 | } 95 | if ($issetCss) { 96 | $this->addCss($pluginOptions['css']); 97 | } else { 98 | if ($this->isPluginCssFileExist($pluginName)) { 99 | $this->addCss($this->getDefaultCssUrl($pluginName)); 100 | } 101 | } 102 | } 103 | } 104 | $pluginNames[] = $pluginName; 105 | } 106 | } 107 | } else { 108 | $this->registerPlugins(array_diff($this->froalaPlugins, $excludedPlugins ?: []), true, true); 109 | } 110 | return $pluginNames; 111 | } 112 | 113 | /** 114 | * @param $pluginName 115 | * @param bool $checkJs 116 | * @param bool $checkCss 117 | * @throws Exception 118 | */ 119 | public function registerPlugin($pluginName, $checkJs = true, $checkCss = true) 120 | { 121 | $jsFile = "js/plugins/$pluginName.min.js"; 122 | if ($checkJs && $this->isPluginJsFileExist($pluginName)) { 123 | $this->addJs($jsFile); 124 | $cssFile = "css/plugins/$pluginName.min.css"; 125 | if (!$checkCss || $this->isPluginCssFileExist($pluginName)) { 126 | $this->addCss($cssFile); 127 | } 128 | } else { 129 | $thirdPartyJsFile = "js/third_party/$pluginName.min.js"; 130 | if($checkJs && $this->isThirdPartyPluginJsFileExist($pluginName)) { 131 | $this->addJs($thirdPartyJsFile); 132 | $thirdPartyCssFile = "css/third_party/$pluginName.min.css"; 133 | if (!$checkCss || $this->isThirdPartyPluginCssFileExist($pluginName)) { 134 | $this->addCss($thirdPartyCssFile); 135 | } 136 | } 137 | else { 138 | throw new Exception("plugin '$pluginName' is not supported, if you trying to set custom plugin, please set 'js' and 'css' options for your plugin"); 139 | } 140 | 141 | } 142 | } 143 | 144 | /** 145 | * @param array $pluginsArray 146 | * @param bool $checkJs 147 | * @param bool $checkCss 148 | */ 149 | public function registerPlugins(array $pluginsArray, $checkJs = true, $checkCss = true) 150 | { 151 | foreach ($pluginsArray as $pluginName) { 152 | $this->registerPlugin($pluginName, $checkJs, $checkCss); 153 | } 154 | } 155 | 156 | /** 157 | * @param $pluginName 158 | * @return bool 159 | */ 160 | public function isPluginJsFileExist($pluginName) 161 | { 162 | return is_file($this->sourcePath . '/' . $this->getDefaultJsUrl($pluginName)); 163 | } 164 | 165 | public function isThirdPartyPluginJsFileExist($pluginName) 166 | { 167 | return is_file($this->sourcePath . '/' . $this->getDefaultThirdPartyJsUrl($pluginName)); 168 | } 169 | 170 | /** 171 | * @param $pluginName 172 | * @return bool 173 | */ 174 | public function isPluginCssFileExist($pluginName) 175 | { 176 | return is_file($this->sourcePath . '/' . $this->getDefaultCssUrl($pluginName)); 177 | } 178 | 179 | public function isThirdPartyPluginCssFileExist($pluginName) 180 | { 181 | return is_file($this->sourcePath . '/' . $this->getDefaultThirdPartyCssUrl($pluginName)); 182 | } 183 | 184 | /** 185 | * @param $pluginName 186 | * @param $excludedPlugins 187 | * @return bool 188 | */ 189 | public function isPluginExcluded($pluginName, $excludedPlugins) 190 | { 191 | return in_array($pluginName, $excludedPlugins, true); 192 | } 193 | 194 | /** 195 | * @param $jsFile 196 | */ 197 | public function addJs($jsFile) 198 | { 199 | $this->js[] = $jsFile; 200 | } 201 | 202 | /** 203 | * @param $cssFile 204 | */ 205 | public function addCss($cssFile) 206 | { 207 | $this->css[] = $cssFile; 208 | } 209 | 210 | /** 211 | * @param $pluginName 212 | * @return string 213 | */ 214 | public function getDefaultCssUrl($pluginName) 215 | { 216 | return "css/plugins/{$pluginName}.min.css"; 217 | } 218 | 219 | public function getDefaultThirdPartyCssUrl($pluginName) 220 | { 221 | return "css/third_party/{$pluginName}.min.css"; 222 | } 223 | 224 | /** 225 | * @param $pluginName 226 | * @return string 227 | */ 228 | private function getDefaultJsUrl($pluginName) 229 | { 230 | return "js/plugins/{$pluginName}.min.js"; 231 | } 232 | 233 | private function getDefaultThirdPartyJsUrl($pluginName) 234 | { 235 | return "js/third_party/{$pluginName}.min.js"; 236 | } 237 | } 238 | -------------------------------------------------------------------------------- /src/FroalaEditorWidget.php: -------------------------------------------------------------------------------- 1 | sample input: 18 | * [ 19 | * //specify only needed forala plugins (local files) 20 | * 'url', 21 | * 'align', 22 | * 'char_counter', 23 | * ... 24 | * //override default files for a specific plugin 25 | * 'table' => [ 26 | * 'css' => '' 27 | * ], 28 | * //include custom plugin 29 | * 'my_plugin' => [ 30 | * 'js' => '' // required 31 | * 'css' => '' // optional 32 | * ], 33 | * ... 34 | * ] 35 | */ 36 | public $clientPlugins; 37 | 38 | /** 39 | * Remove these plugins from this list plugins, this option overrides 'clientPlugins' 40 | * @var array 41 | */ 42 | public $excludedPlugins = []; 43 | 44 | /** 45 | * FroalaEditor Options 46 | * @var array 47 | */ 48 | public $clientOptions = []; 49 | 50 | /** 51 | * csrf cookie param 52 | * @var string 53 | */ 54 | public $csrfCookieParam = '_csrfCookie'; 55 | 56 | /** 57 | * @var boolean 58 | */ 59 | public $render = true; 60 | 61 | /** 62 | * @inheritdoc 63 | */ 64 | public function run() 65 | { 66 | if ($this->render) { 67 | if ($this->hasModel()) { 68 | echo Html::activeTextarea($this->model, $this->attribute, $this->options); 69 | } else { 70 | echo Html::textarea($this->name, $this->value, $this->options); 71 | } 72 | } 73 | $this->registerClientScript(); 74 | } 75 | 76 | /** 77 | * register client scripts(css, javascript) 78 | */ 79 | public function registerClientScript() 80 | { 81 | $view = $this->getView(); 82 | $asset = FroalaEditorAsset::register($view); 83 | $plugin_names = $asset->registerClientPlugins($this->clientPlugins, $this->excludedPlugins); 84 | 85 | //theme 86 | $themeType = isset($this->clientOptions['theme']) ? $this->clientOptions['theme'] : 'default'; 87 | if ($themeType != 'default') { 88 | $view->registerCssFile("{$asset->baseUrl}/css/themes/{$themeType}.css", ['depends' => '\froala\froalaeditor\FroalaEditorAsset']); 89 | } 90 | //language 91 | $langType = isset($this->clientOptions['language']) ? $this->clientOptions['language'] : 'en_gb'; 92 | if ($langType != 'es_gb') { 93 | $view->registerJsFile("{$asset->baseUrl}/js/languages/{$langType}.js", ['depends' => '\froala\froalaeditor\FroalaEditorAsset']); 94 | } 95 | 96 | $id = $this->options['id']; 97 | if (empty($this->clientPlugins)) { 98 | $pluginsEnabled = false; 99 | } else { 100 | $pluginsEnabled = array_diff($plugin_names, $this->excludedPlugins ?: []); 101 | } 102 | if(!empty($pluginsEnabled)){ 103 | foreach($pluginsEnabled as $key =>$item){ 104 | $pluginsEnabled[$key] = lcfirst (yii\helpers\Inflector::camelize($item)); 105 | } 106 | } 107 | 108 | $jsOptions = array_merge($this->clientOptions, $pluginsEnabled ? ['pluginsEnabled' => $pluginsEnabled] : []); 109 | $jsOptions = Json::encode($jsOptions); 110 | 111 | $view->registerJs("new FroalaEditor('#$id',$jsOptions);"); 112 | } 113 | } 114 | --------------------------------------------------------------------------------