├── .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 | [](https://packagist.org/packages/froala/yii2-froala-editor)
4 | [](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 | = \froala\froalaeditor\FroalaEditorWidget::widget([
133 | 'name' => '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 | = \Froala\Editor\FroalaEditorWidget::widget([
159 | 'name' => '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 |
--------------------------------------------------------------------------------