├── CMSAsset.php ├── Module.php ├── README.md ├── assets ├── BlockUIAsset.php ├── BootBoxAsset.php ├── BootstrapHoverDropdownAsset.php ├── CookiesAsset.php ├── FancyboxAsset.php ├── FastClickAsset.php ├── ImageAsset.php ├── MediaAsset.php ├── MoxieManagerAsset.php ├── NestableAsset.php ├── PerfectScrollbarAsset.php ├── UnderscoreAsset.php ├── XEditableAsset.php ├── css │ ├── bootstrap.min.css │ ├── images.css │ ├── main.css │ ├── media.css │ ├── rtl.css │ └── sb-admin-2.css ├── img │ ├── avatar.png │ ├── brand-logo.png │ ├── favicon.ico │ ├── flexmail.png │ ├── loading.gif │ ├── logo-infoweb.png │ ├── logo-iw-dark.png │ ├── placeholder.png │ └── transparent-placeholder.png └── js │ ├── cms.js │ ├── i18n.js │ ├── images.js │ ├── jquery-plugins │ └── jquery.duplicateable.js │ ├── jquery.blockUI.js │ ├── main.js │ └── media.js ├── behaviors ├── Base64EncodeBehavior.php ├── FileBehave.php ├── HomepageBehavior.php ├── ImageBehave.php └── PdfBehave.php ├── components └── Frontend.php ├── composer.json ├── config.php ├── controllers ├── ImageController.php ├── MediaController.php └── ParseController.php ├── helpers ├── ArrayHelper.php ├── CMS.php ├── Inflector.php └── LanguageHelper.php ├── mail ├── contact.php └── layouts │ └── html.php ├── messages └── nl │ ├── app.php │ └── infoweb │ └── cms.php ├── migrations ├── m140930_123135_create_default_auth.php ├── m141001_090124_add_default_permissions.php ├── m150107_074600_create_image_tables.php ├── m150526_084143_update_image_table.php ├── m150602_110727_add_identifier_field.php ├── m151012_130517_add_account_permissions.php ├── m151118_084144_update_image_lang_table.php └── m160614_090913_add_position_column.php ├── models ├── Image.php ├── ImageLang.php ├── ImageSearch.php ├── ImageUploadForm.php ├── MaskMoney.php └── PlaceHolder.php └── views ├── layouts ├── _menu_sidebar.php ├── _menu_top_right.php ├── main.php └── media.php ├── media ├── index.php └── media.php └── ui └── formButtons.php /CMSAsset.php: -------------------------------------------------------------------------------- 1 | 300, 40 | 'preset' => 'custom', 41 | 'toolbarGroups' => [ 42 | ['name' => 'clipboard', 'groups' => ['mode','undo', 'selection', 'clipboard', 'doctools']], 43 | ['name' => 'editing', 'groups' => ['tools']], 44 | ['name' => 'paragraph', 'groups' => ['templates', 'list', 'indent', 'align']], 45 | ['name' => 'insert'], 46 | ['name' => 'basicstyles', 'groups' => ['basicstyles', 'cleanup']], 47 | ['name' => 'colors'], 48 | ['name' => 'links'], 49 | ['name' => 'others'], 50 | ['name' => 'styles'] 51 | ], 52 | 'removeButtons' => 'Smiley,Iframe,Templates,Outdent,Indent,Flash,SpecialChar,PageBreak,Font,FontSize', 53 | 'allowedContent' => true, 54 | 'extraPlugins' => 'codemirror,moxiemanager,dialogui,dialog,lineutils,widget,entitylink', 55 | 'enterMode' => 2, 56 | 'stylesSet' => [], 57 | ]; 58 | 59 | /** 60 | * @var array The cached stylesheets for the ckeditor 61 | */ 62 | private $_ckEditorStylesheets = []; 63 | 64 | public function init() 65 | { 66 | parent::init(); 67 | 68 | // Gridview default settings 69 | $gridviewSettings = [ 70 | 'export' => false, 71 | 'responsive' => true, 72 | 'floatHeader' => false, 73 | //'floatHeaderOptions' => ['top' => 88], 74 | 'hover' => true, 75 | 'pjax' => true, 76 | 'pjaxSettings' => [ 77 | 'options' => [ 78 | 'id' => 'grid-pjax', 79 | ], 80 | ], 81 | 'resizableColumns' => false, 82 | ]; 83 | 84 | Yii::$container->set('kartik\grid\GridView', $gridviewSettings); 85 | Yii::$container->set('infoweb\sortable\SortableGridView', $gridviewSettings); 86 | 87 | // Fileinput default settings 88 | $fileInputSettings = [ 89 | 'pluginOptions' => [ 90 | 'showPreview' => true, 91 | 'showCaption' => true, 92 | 'showRemove' => true, 93 | 'showUpload' => false, 94 | 'browseLabel' => Yii::t('app', 'Browse'), 95 | 'removeLabel' => Yii::t('app', 'Remove'), 96 | 'removeTitle' => Yii::t('app', 'Remove selected files'), 97 | 'uploadLabel' => Yii::t('app', 'Upload'), 98 | 'uploadTitle' => Yii::t('app', 'Upload selected files'), 99 | 'cancelLabel' => Yii::t('app', 'Cancel'), 100 | ], 101 | ]; 102 | 103 | Yii::$container->set('kartik\widgets\FileInput', $fileInputSettings); 104 | 105 | // Initialize moxiemanager session vars 106 | $this->initMoxiemanagerSession(); 107 | } 108 | 109 | /** 110 | * Returns the items for the sidebar, formatted for usage in the SideNav widget 111 | * 112 | * @param string $group The group that contains the items 113 | * @param string $template The template that is used to render the html of the items 114 | * @return array 115 | */ 116 | public function getSideBarItems($group = '', $template = '{icon}{label}') 117 | { 118 | $items = []; 119 | 120 | if (!isset($this->sideBarItems[$group])) 121 | return $items; 122 | 123 | foreach ($this->sideBarItems[$group] as $item) { 124 | $items[] = [ 125 | 'label' => Yii::t($item['i18nGroup'], $item['label']), 126 | 'url' => Url::toRoute($item['url']), 127 | 'template' => $template, 128 | 'visible' => (isset($item['authItem']) && Yii::$app->user->can($item['authItem'])) ? true : false, 129 | 'active' => (stripos(Yii::$app->request->url, (isset($item['activeUrl'])) ? $item['activeUrl'] : $item['url']) !== false) ? true : false 130 | ]; 131 | } 132 | 133 | return $items; 134 | } 135 | 136 | public function getCKEditorStylesheets() 137 | { 138 | // No cached version found 139 | if (!$this->_ckEditorStylesheets) { 140 | 141 | // Get the asset url's 142 | $bootstrapAsset = BootstrapAsset::register(Yii::$app->view); 143 | $fontAwesome = FontAwesomeAsset::register(Yii::$app->view); 144 | 145 | // Add default css 146 | $css = [ 147 | $bootstrapAsset->baseUrl . '/css/bootstrap.min.css', 148 | Yii::getAlias('@frontendUrl') . '/css/main.css', 149 | Yii::getAlias('@frontendUrl') . '/css/editor.css', 150 | $fontAwesome->baseUrl . '/' . $fontAwesome->css[0], 151 | ]; 152 | 153 | // Add font assets if they exist 154 | if (class_exists('\frontend\assets\FontAsset')) { 155 | // Get the font asset 156 | $fontAsset = new FontAsset; 157 | 158 | // Add google fonts 159 | foreach ($fontAsset->css as $font) { 160 | $css[] = $fontAsset->basePath . '/' . $font; 161 | } 162 | } 163 | 164 | $this->_ckEditorStylesheets = $css; 165 | } 166 | 167 | return $this->_ckEditorStylesheets; 168 | } 169 | 170 | public function getCKEditorOptions() 171 | { 172 | return ArrayHelper::merge($this->ckEditorOptions, ['contentsCss' => $this->getCKEditorStylesheets()]); 173 | } 174 | 175 | public function initMoxiemanagerSession() 176 | { 177 | $session = new Session; 178 | $session->open(); 179 | $session->name = $this->sessionName; 180 | $session['moxieman-is-logged-in'] = true; 181 | $session['moxieman-user'] = 'infoweb'; 182 | $session['moxieman-license-key'] = Yii::$app->params['moxiemanager']['license-key']; 183 | $session['moxieman-filesystem-rootpath'] = Yii::getAlias('@uploadsBasePath'); 184 | $session['moxieman-filesystem-wwwroot'] = Yii::getAlias('@basePath'); 185 | $session['moxieman-filesystem-urlprefix'] = YII_DEBUG ? Yii::getAlias('@baseUrl') : '/'; 186 | } 187 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | CMS module for Yii 2 2 | ======================== 3 | 4 | Installation 5 | ------------ 6 | 7 | The preferred way to install this extension is through [composer](http://getcomposer.org/download/). 8 | 9 | You can then install the application using the following command: 10 | 11 | ```bash 12 | php composer.phar global require "fxp/composer-asset-plugin:~1.0" 13 | php composer.phar create-project --prefer-dist --stability=dev yiisoft/yii2-app-advanced advanced 14 | ``` 15 | 16 | ### Configure environments 17 | Create a new dev and production database and adjust the `components['db']` configuration in `environments/dev/common/config/main-local.php` and `environments/prod/common/config/main-local.php` accordingly. 18 | 19 | Remove the `components['mailer']` configuration from both files because it will be added to `common/config/main.php` 20 | 21 | Add to `environments/prod/common/config/main-local.php` 22 | ```php 23 | 'controllerMap' => [ 24 | 'migrate' => [ 25 | 'class' => 'fishvision\migrate\controllers\MigrateController', 26 | 'autoDiscover' => true, 27 | 'migrationPaths' => [ 28 | '@vendor' 29 | ], 30 | ], 31 | ], 32 | ``` 33 | 34 | ### Update composer.json file 35 | Update the `config` section of `composer.json` if you want composer to download git folders for the packages 36 | ```json 37 | "config": { 38 | ... 39 | "preferred-install": "source" 40 | }, 41 | ``` 42 | 43 | Add the `infoweb-internet-solutions/yii2-cms` and `fishvision/yii2-migrate` packages 44 | ```php 45 | "require": [ 46 | ... 47 | "fishvision/yii2-migrate": "*", 48 | "infoweb-internet-solutions/yii2-cms": "*" 49 | ] 50 | ``` 51 | 52 | Add references to the custom repositories that are needed to override certain vendor packages 53 | ```php 54 | ... 55 | "repositories": [ 56 | { 57 | "type": "vcs", 58 | "url": "https://github.com/infoweb-internet-solutions/yii2-i18n-module" 59 | }, 60 | { 61 | "type": "vcs", 62 | "url": "https://github.com/infoweb-internet-solutions/yii2-ckeditor" 63 | } 64 | ] 65 | ... 66 | ``` 67 | 68 | Add following rule to filter bower/npm packages that can be skipped (it will make sure you won't get a out of memory and it will decrease the needed time to update your project). 69 | ```php 70 | ... 71 | "extra": { 72 | ... 73 | "asset-pattern-skip-version": "(-patch)" 74 | } 75 | ``` 76 | 77 | Check if `"minimum-stability": "dev"` is set 78 | 79 | After this run `composer update` to install the package 80 | 81 | ### Init environment 82 | 83 | Create folders in `frontend/web/` 84 | ``` 85 | uploads/img 86 | uploads/files 87 | ``` 88 | 89 | and add `.gitignore` file in `uploads/` 90 | 91 | ``` 92 | * 93 | !.gitignore 94 | ``` 95 | 96 | Remove `adminEmail` and `supportEmail` in `backend/config/params.php`, `common/config/params.php`, `frontend/config/params.php` and `console/config/params.php` and at them to `environments/dev/common/config/params-local.php` and `environments/prod/common/config/params-local.php` 97 | 98 | Run command `init` to initialize the application with a specific environment. 99 | 100 | Usage 101 | ----- 102 | 103 | Once the extension is installed, simply modify `common/config/main.php` as follows: 104 | 105 | ```php 106 | 107 | use \kartik\datecontrol\Module; 108 | 109 | return [ 110 | ... 111 | 'name' => 'My Application', 112 | 'language' => 'nl', 113 | 'timeZone' => 'Europe/Brussels', 114 | ... 115 | 'components' => [ 116 | ... 117 | 'cache' => [ 118 | 'class' => 'yii\caching\DbCache', 119 | ], 120 | 'authManager' => [ 121 | 'class' => 'yii\rbac\DbManager', 122 | ], 123 | // Rewrite url's 124 | 'urlManager' => [ 125 | 'enablePrettyUrl' => true, 126 | 'showScriptName' => false, 127 | ], 128 | // Formatter 129 | 'formatter' => [ 130 | 'dateFormat' => 'php:d-m-Y', 131 | 'decimalSeparator' => ',', 132 | 'thousandSeparator' => ' ', 133 | 'currencyCode' => 'EUR', 134 | ], 135 | // Override views 136 | 'view' => [ 137 | 'theme' => [ 138 | 'pathMap' => [ 139 | '@dektrium/user/views' => '@infoweb/user/views' 140 | ], 141 | ], 142 | ], 143 | 'mailer' => [ 144 | 'class' => 'yii\swiftmailer\Mailer', 145 | 'viewPath' => '@infoweb/cms/mail', 146 | // send all mails to a file by default. You have to set 147 | // 'useFileTransport' to false and configure a transport 148 | // for the mailer to send real emails. 149 | 'useFileTransport' => false, 150 | 'transport' => [ 151 | 'class' => 'Swift_SmtpTransport', 152 | 'host' => 'host', 153 | 'username' => 'user', 154 | 'password' => 'password', 155 | 'port' => 'port' 156 | ], 157 | ], 158 | 'log' => [ 159 | 'traceLevel' => YII_DEBUG ? 3 : 0, 160 | 'targets' => [ 161 | [ 162 | 'class' => 'yii\log\FileTarget', 163 | 'levels' => ['error', 'warning'], 164 | ], 165 | [ 166 | 'class' => 'yii\log\DbTarget', 167 | 'levels' => ['error'], 168 | ], 169 | [ 170 | 'class' => 'yii\log\EmailTarget', 171 | 'levels' => ['error'], 172 | 'categories' => ['yii\db\*'], 173 | 'message' => [ 174 | 'from' => ['info@domain.com'], 175 | 'to' => ['developer@domain.com'], 176 | 'subject' => '[MySQL error @ domain.com]', 177 | ], 178 | ], 179 | ], 180 | ], 181 | 'i18n' => [ 182 | 'class' => Zelenin\yii\modules\I18n\components\I18N::className(), 183 | 'languages' => ['nl'] 184 | ], 185 | ], 186 | ... 187 | 'modules' => [ 188 | 'datecontrol' => [ 189 | 'class' => 'kartik\datecontrol\Module', 190 | 191 | // format settings for displaying each date attribute (ICU format example) 192 | 'displaySettings' => [ 193 | Module::FORMAT_DATE => 'php:d-m-Y', 194 | Module::FORMAT_TIME => 'php:H:i', 195 | Module::FORMAT_DATETIME => 'dd-MM-yyyy HH:mm:ss', 196 | ], 197 | 198 | // format settings for saving each date attribute (PHP format example) 199 | 'saveSettings' => [ 200 | Module::FORMAT_DATE => 'php:U', // saves as unix timestamp 201 | Module::FORMAT_TIME => 'php:H:i:s', 202 | Module::FORMAT_DATETIME => 'php:Y-m-d H:i:s', 203 | ], 204 | 205 | // set your display timezone 206 | 'displayTimezone' => 'Europe/Brussels', 207 | 208 | // set your timezone for date saved to db 209 | 'saveTimezone' => 'Europe/Brussels', 210 | 211 | // automatically use kartik\widgets for each of the above formats 212 | 'autoWidget' => true, 213 | 214 | // default settings for each widget from kartik\widgets used when autoWidget is true 215 | 'autoWidgetSettings' => [ 216 | Module::FORMAT_DATE => ['pluginOptions' => [ 217 | 'autoclose' => true, 218 | 'todayHighlight' => true, 219 | //'todayBtn' => true 220 | ]], 221 | Module::FORMAT_DATETIME => [], // setup if needed 222 | Module::FORMAT_TIME => [], // setup if needed 223 | ], 224 | // Use custom convert action 225 | 'convertAction' => '/cms/parse/convert-date-control' 226 | ], 227 | 'yii2images' => [ 228 | 'class' => 'rico\yii2images\Module', 229 | 'imagesStorePath' => '@uploadsBasePath/img', //path to origin images 230 | 'imagesCachePath' => '@uploadsBasePath/img/cache', //path to resized copies 231 | 'graphicsLibrary' => 'GD', //but really its better to use 'Imagick' 232 | 'placeHolderPath' => '@infoweb/cms/assets/img/transparent-placeholder.png', 233 | ], 234 | ], 235 | ... 236 | 'params' => [ 237 | // Font Awesome Icon framework 238 | 'icon-framework' => 'fa', 239 | ], 240 | ]; 241 | ``` 242 | (dont forget to update the settings of the **mailer**, **log** and **i18n** components!) 243 | 244 | `backend/config/main.php` as follows: 245 | 246 | ```php 247 | return [ 248 | ... 249 | 'name' => 'My application', 250 | ... 251 | 'bootstrap' => ['log','cms'], 252 | ... 253 | 'modules' => [ 254 | ... 255 | 'cms' => [ 256 | 'class' => 'infoweb\cms\Module', 257 | ], 258 | 'gridview' => [ 259 | 'class' => '\kartik\grid\Module' 260 | ], 261 | 'media' => [ 262 | 'class' => 'infoweb\cms\Module', 263 | ], 264 | 'email' => [ 265 | 'class' => 'infoweb\email\Module' 266 | ], 267 | 'admin' => [ 268 | 'class' => 'mdm\admin\Module', 269 | ], 270 | 'i18n' => [ 271 | 'class' => Zelenin\yii\modules\I18n\Module::className(), 272 | ], 273 | 'settings' => [ 274 | 'class' => 'infoweb\settings\Module' 275 | ], 276 | 'pages' => [ 277 | 'class' => 'infoweb\pages\Module', 278 | ], 279 | 'partials' => [ 280 | 'class' => 'infoweb\partials\Module', 281 | ], 282 | 'seo' => [ 283 | 'class' => 'infoweb\seo\Module', 284 | ], 285 | 'menu' => [ 286 | 'class' => 'infoweb\menu\Module', 287 | ], 288 | 'alias' => [ 289 | 'class' => 'infoweb\alias\Module', 290 | 'reservedUrls' => ['page'] // Url's that are reserved by the application 291 | ], 292 | ], 293 | ... 294 | 'components' => [ 295 | ... 296 | 'view' => [ 297 | 'theme' => [ 298 | 'pathMap' => [ 299 | '@app/views/layouts' => '@infoweb/cms/views/layouts', 300 | '@dektrium/user/views' => '@infoweb/user/views' 301 | ], 302 | ], 303 | ], 304 | 'request' => [ 305 | 'class' => 'common\components\Request', 306 | 'web'=> '/backend/web', 307 | 'adminUrl' => '/admin' 308 | ], 309 | ], 310 | ... 311 | ]; 312 | ``` 313 | 314 | `backend/config/params.php` as follows: 315 | ```php 316 | return [ 317 | ... 318 | // Moximanager settings 319 | 'moxiemanager' => [ 320 | 'license-key' => 'your-moxiemanager-key' 321 | ], 322 | ... 323 | ] 324 | ``` 325 | 326 | and `common/config/params.php` as follows: 327 | 328 | ```php 329 | return [ 330 | ... 331 | // Enabled languages 332 | 'languages' => [ 333 | 'nl' => 'Nederlands', 334 | 'fr' => 'Français', 335 | 'en' => 'English', 336 | ], 337 | 'companyName' => 'YourCompany' 338 | ... 339 | ]; 340 | ``` 341 | 342 | and `frontend/config/main.php` as follows: 343 | 344 | ```php 345 | return [ 346 | ... 347 | 'components' => [ 348 | 'user' => [ 349 | 'identityClass' => 'infoweb\user\models\frontend\User', 350 | 'enableAutoLogin' => true, 351 | ], 352 | 'log' => [ 353 | 'traceLevel' => YII_DEBUG ? 3 : 0, 354 | 'targets' => [ 355 | [ 356 | 'class' => 'yii\log\FileTarget', 357 | 'levels' => ['error', 'warning'], 358 | ], 359 | ], 360 | ], 361 | 'errorHandler' => [ 362 | 'errorAction' => 'site/error', 363 | ], 364 | 'request'=>[ 365 | 'class' => 'common\components\Request', 366 | 'web' => '/frontend/web', 367 | 'csrfParam' => '_frontendCSRF', 368 | ], 369 | // Override the urlManager component 370 | 'urlManager' => [ 371 | 'class' => 'codemix\localeurls\UrlManager', 372 | 'enablePrettyUrl' => true, 373 | 'showScriptName' => false, 374 | 'rules' => [ 375 | '404' => 'site/error', 376 | '' => 'site/index', 377 | ], 378 | ], 379 | 'page' => [ 380 | 'class' => 'infoweb\pages\components\Page' 381 | ] 382 | ], 383 | ... 384 | ]; 385 | ``` 386 | 387 | 388 | Docs 389 | ----- 390 | 391 | Follow all usage instructions 392 | Do not run composer, all modules are included in the infoweb-cms composer file and should be already installed 393 | Do not run any migrations and don't import messages, we'll do this later 394 | 395 | - [Installation user module](https://github.com/infoweb-internet-solutions/yii2-cms-user) 396 | - [Installation analytics widget](https://github.com/infoweb-internet-solutions/yii2-cms-analytics) 397 | 398 | 399 | 400 | Add a couple of system aliases to `common/config/bootstrap.php` 401 | ```php 402 | ... 403 | // System aliases 404 | Yii::setAlias('baseUrl', 'http://' . ((isset($_SERVER['HTTP_HOST'])) ? $_SERVER['HTTP_HOST'] : '') . ((YII_ENV_DEV) ? '/directory-in-your-localhost' : '')); <-- Change your path 405 | Yii::setAlias('basePath', dirname(dirname(__DIR__))); 406 | Yii::setAlias('uploadsBaseUrl', Yii::getAlias('@baseUrl') . '/frontend/web/uploads'); 407 | Yii::setAlias('uploadsBasePath', Yii::getAlias('@basePath') . '/frontend/web/uploads'); 408 | Yii::setAlias('frontendUrl', Yii::getAlias('@baseUrl') . '/frontend/web'); 409 | ... 410 | ``` 411 | 412 | Apply migrations with console commands. This will create tables needed for the application to work. 413 | Add to `commonfig/config/main.php` to run all migrations and remove this afterwards. 414 | ```php 415 | 'controllerMap' => [ 416 | 'migrate' => [ 417 | 'class' => 'fishvision\migrate\controllers\MigrateController', 418 | 'autoDiscover' => true, 419 | 'migrationPaths' => [ 420 | '@vendor' 421 | ], 422 | ], 423 | ], 424 | ``` 425 | ```bash 426 | yii migrate/up 427 | ``` 428 | 429 | Import the translations 430 | ```bash 431 | yii i18n/import @infoweb/cms/messages --interactive=0 432 | yii i18n/import @Zelenin/yii/modules/I18n/messages --interactive=0 433 | yii i18n/import @infoweb/settings/messages --interactive=0 434 | yii i18n/import @infoweb/pages/messages --interactive=0 435 | yii i18n/import @infoweb/partials/messages --interactive=0 436 | yii i18n/import @infoweb/seo/messages --interactive=0 437 | yii i18n/import @infoweb/alias/messages --interactive=0 438 | yii i18n/import @infoweb/analytics/messages --interactive=0 439 | yii i18n/import @infoweb/email/messages --interactive=0 440 | yii i18n/import @infoweb/sliders/messages --interactive=0 441 | ``` 442 | Yii messages import fails sometimes, try importing without --interactive=0 and keep hitting return until it's done 443 | ```bash 444 | yii i18n/import @yii/messages 445 | 446 | ``` 447 | 448 | Add htaccess files 449 | 450 | Root 451 | 452 | ```apache 453 | 454 | Options +FollowSymlinks 455 | RewriteEngine On 456 | 457 | 458 | 459 | # deal with admin first 460 | RewriteCond %{REQUEST_URI} ^/(admin) 461 | RewriteRule ^admin/assets/(.*)$ backend/web/assets/$1 [L] 462 | RewriteRule ^admin/css/(.*)$ backend/web/css/$1 [L] 463 | RewriteRule ^admin/js/(.*)$ backend/web/js/$1 [L] 464 | 465 | RewriteCond %{REQUEST_URI} !^/backend/web/(assets|css)/ 466 | RewriteCond %{REQUEST_URI} ^/(admin) 467 | RewriteRule ^.*$ backend/web/index.php [L] 468 | 469 | RewriteCond %{REQUEST_URI} ^/(assets|css) 470 | RewriteRule ^assets/(.*)$ frontend/web/assets/$1 [L] 471 | RewriteRule ^css/(.*)$ frontend/web/css/$1 [L] 472 | RewriteRule ^js/(.*)$ frontend/web/js/$1 [L] 473 | 474 | RewriteCond %{REQUEST_URI} !^/(frontend|backend)/web/(assets|css)/ 475 | RewriteCond %{REQUEST_URI} !index.php 476 | RewriteCond %{REQUEST_URI} !^/preview 477 | RewriteCond %{REQUEST_FILENAME} !-f [OR] 478 | RewriteCond %{REQUEST_FILENAME} !-d 479 | RewriteRule ^.*$ frontend/web/index.php 480 | 481 | 482 | # Optional 483 | 484 | # Upload limit 485 | php_value upload_max_filesize 200M 486 | php_value post_max_size 200M 487 | php_value max_execution_time 6000 488 | php_value max_input_time 2400 489 | php_value max_file_uploads 100 490 | 491 | 492 | php_value memory_limit 256M 493 | 494 | ``` 495 | 496 | backend/web/ 497 | 498 | ```apache 499 | RewriteEngine on 500 | 501 | # if a directory or a file exists, use it directly 502 | RewriteCond %{REQUEST_FILENAME} !-f 503 | RewriteCond %{REQUEST_FILENAME} !-d 504 | 505 | # otherwise forward it to index.php 506 | RewriteRule . index.php 507 | 508 | Options FollowSymLinks 509 | ``` 510 | 511 | frontend/web/ 512 | 513 | ```apache 514 | RewriteEngine on 515 | 516 | RewriteCond %{REQUEST_FILENAME} !-f 517 | RewriteCond %{REQUEST_FILENAME} !-d 518 | RewriteRule ^(.+)/$ http://%{HTTP_HOST}/$1 [R=301,L] # Remove trailing slash 519 | 520 | RewriteCond %{REQUEST_FILENAME} !-f 521 | RewriteCond %{REQUEST_FILENAME} !-d 522 | RewriteRule . index.php 523 | ``` 524 | 525 | 526 | Add new file`frontend/web/css/editor.css` 527 | ```css 528 | body { 529 | padding: 15px; 530 | } 531 | ``` 532 | 533 | 534 | 535 | 536 | Add new class in `common/components/Request.php` 537 | 538 | ```php 539 | [ 553 | * 'class' => 'common\components\Request', 554 | * 'web'=> '/frontend/web' 555 | * ] 556 | * 557 | * 558 | */ 559 | class Request extends \yii\web\Request 560 | { 561 | public $web; 562 | public $adminUrl; 563 | 564 | /** 565 | * Takes the base url from the parent class and replaces the 'web' url that 566 | * you defined with an empty string: 567 | * 568 | * eg: the 'web' url is set to 'frontend/web' 569 | * www.mydomain.com/frontend/web becomes www.mydomain.com/ 570 | * 571 | * @return string 572 | */ 573 | public function getBaseUrl() 574 | { 575 | return str_replace($this->web, '', parent::getBaseUrl()) . $this->adminUrl; 576 | } 577 | 578 | 579 | /** 580 | * This function ensures that www.mydomain.com/admin (without trailing slash) will not 581 | * throw a 404 error 582 | * 583 | * @return string 584 | */ 585 | public function resolvePathInfo() 586 | { 587 | if ($this->getUrl() === $this->adminUrl) { 588 | return ''; 589 | } else { 590 | return parent::resolvePathInfo(); 591 | } 592 | } 593 | } 594 | ``` 595 | 596 | Add to gitignore 597 | ``` 598 | # Yii files 599 | README.md 600 | LICENSE.md 601 | requirements.php 602 | ``` 603 | 604 | Add the following to `console/config/main.php` to enable creation of users via the cli: 605 | ```php 606 | 'modules' => [ 607 | 'user' => [ 608 | 'class' => 'dektrium\user\Module', 609 | 'modelMap' => [ 610 | 'User' => 'infoweb\user\models\User', 611 | ], 612 | ], 613 | ], 614 | ``` 615 | 616 | You can then create the superadmin user with the following command: 617 | ```bash 618 | yii user/create email@domain.com username password 619 | ``` 620 | 621 | Login @ `/admin` and enjoy! 622 | -------------------------------------------------------------------------------- /assets/BlockUIAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 2.0 12 | */ 13 | class BlockUIAsset extends AssetBundle 14 | { 15 | public $sourcePath = '@infoweb/cms/assets/js'; 16 | public $js = [ 17 | 'jquery.blockUI.js', 18 | ]; 19 | } 20 | -------------------------------------------------------------------------------- /assets/BootBoxAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 2.0 12 | */ 13 | class BootstrapHoverDropdownAsset extends AssetBundle 14 | { 15 | public $sourcePath = '@bower/bootstrap-hover-dropdown/'; 16 | public $css = []; 17 | public $js = [ 18 | 'bootstrap-hover-dropdown.min.js' 19 | ]; 20 | } 21 | -------------------------------------------------------------------------------- /assets/CookiesAsset.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 2.0 12 | */ 13 | class FancyboxAsset extends AssetBundle 14 | { 15 | public $sourcePath = '@bower/fancybox/source'; 16 | public $css = [ 17 | 'jquery.fancybox.css' 18 | ]; 19 | public $js = [ 20 | 'jquery.fancybox.pack.js' 21 | ]; 22 | } 23 | -------------------------------------------------------------------------------- /assets/FastClickAsset.php: -------------------------------------------------------------------------------- 1 | a>.fa.arrow:before { 110 | content: "\f107"; 111 | } 112 | 113 | .sidebar .nav-second-level li, 114 | .sidebar .nav-third-level li { 115 | border-bottom: 0!important; 116 | } 117 | 118 | .sidebar .nav-second-level li a { 119 | padding-left: 37px; 120 | } 121 | 122 | .sidebar .nav-third-level li a { 123 | padding-left: 52px; 124 | } 125 | 126 | @media(min-width:768px) { 127 | .sidebar { 128 | z-index: 1; 129 | position: absolute; 130 | width: 250px; 131 | margin-top: 51px; 132 | } 133 | 134 | .navbar-top-links .dropdown-messages, 135 | .navbar-top-links .dropdown-tasks, 136 | .navbar-top-links .dropdown-alerts { 137 | margin-left: auto; 138 | } 139 | } 140 | 141 | .btn-outline { 142 | color: inherit; 143 | background-color: transparent; 144 | transition: all .5s; 145 | } 146 | 147 | .btn-primary.btn-outline { 148 | color: #428bca; 149 | } 150 | 151 | .btn-success.btn-outline { 152 | color: #5cb85c; 153 | } 154 | 155 | .btn-info.btn-outline { 156 | color: #5bc0de; 157 | } 158 | 159 | .btn-warning.btn-outline { 160 | color: #f0ad4e; 161 | } 162 | 163 | .btn-danger.btn-outline { 164 | color: #d9534f; 165 | } 166 | 167 | .btn-primary.btn-outline:hover, 168 | .btn-success.btn-outline:hover, 169 | .btn-info.btn-outline:hover, 170 | .btn-warning.btn-outline:hover, 171 | .btn-danger.btn-outline:hover { 172 | color: #fff; 173 | } 174 | 175 | .chat { 176 | margin: 0; 177 | padding: 0; 178 | list-style: none; 179 | } 180 | 181 | .chat li { 182 | margin-bottom: 10px; 183 | padding-bottom: 5px; 184 | border-bottom: 1px dotted #999; 185 | } 186 | 187 | .chat li.left .chat-body { 188 | margin-left: 60px; 189 | } 190 | 191 | .chat li.right .chat-body { 192 | margin-right: 60px; 193 | } 194 | 195 | .chat li .chat-body p { 196 | margin: 0; 197 | } 198 | 199 | .panel .slidedown .glyphicon, 200 | .chat .glyphicon { 201 | margin-right: 5px; 202 | } 203 | 204 | .chat-panel .panel-body { 205 | height: 350px; 206 | overflow-y: scroll; 207 | } 208 | 209 | .login-panel { 210 | margin-top: 25%; 211 | } 212 | 213 | .flot-chart { 214 | display: block; 215 | height: 400px; 216 | } 217 | 218 | .flot-chart-content { 219 | width: 100%; 220 | height: 100%; 221 | } 222 | 223 | table.dataTable thead .sorting, 224 | table.dataTable thead .sorting_asc, 225 | table.dataTable thead .sorting_desc, 226 | table.dataTable thead .sorting_asc_disabled, 227 | table.dataTable thead .sorting_desc_disabled { 228 | background: 0 0; 229 | } 230 | 231 | table.dataTable thead .sorting_asc:after { 232 | content: "\f0de"; 233 | float: right; 234 | font-family: fontawesome; 235 | } 236 | 237 | table.dataTable thead .sorting_desc:after { 238 | content: "\f0dd"; 239 | float: right; 240 | font-family: fontawesome; 241 | } 242 | 243 | table.dataTable thead .sorting:after { 244 | content: "\f0dc"; 245 | float: right; 246 | font-family: fontawesome; 247 | color: rgba(50,50,50,.5); 248 | } 249 | 250 | .btn-circle { 251 | width: 30px; 252 | height: 30px; 253 | padding: 6px 0; 254 | border-radius: 15px; 255 | text-align: center; 256 | font-size: 12px; 257 | line-height: 1.428571429; 258 | } 259 | 260 | .btn-circle.btn-lg { 261 | width: 50px; 262 | height: 50px; 263 | padding: 10px 16px; 264 | border-radius: 25px; 265 | font-size: 18px; 266 | line-height: 1.33; 267 | } 268 | 269 | .btn-circle.btn-xl { 270 | width: 70px; 271 | height: 70px; 272 | padding: 10px 16px; 273 | border-radius: 35px; 274 | font-size: 24px; 275 | line-height: 1.33; 276 | } 277 | 278 | .show-grid [class^=col-] { 279 | padding-top: 10px; 280 | padding-bottom: 10px; 281 | border: 1px solid #ddd; 282 | background-color: #eee!important; 283 | } 284 | 285 | .show-grid { 286 | margin: 15px 0; 287 | } 288 | 289 | .huge { 290 | font-size: 40px; 291 | } 292 | 293 | .panel-green { 294 | border-color: #5cb85c; 295 | } 296 | 297 | .panel-green .panel-heading { 298 | border-color: #5cb85c; 299 | color: #fff; 300 | background-color: #5cb85c; 301 | } 302 | 303 | .panel-green a { 304 | color: #5cb85c; 305 | } 306 | 307 | .panel-green a:hover { 308 | color: #3d8b3d; 309 | } 310 | 311 | .panel-red { 312 | border-color: #d9534f; 313 | } 314 | 315 | .panel-red .panel-heading { 316 | border-color: #d9534f; 317 | color: #fff; 318 | background-color: #d9534f; 319 | } 320 | 321 | .panel-red a { 322 | color: #d9534f; 323 | } 324 | 325 | .panel-red a:hover { 326 | color: #b52b27; 327 | } 328 | 329 | .panel-yellow { 330 | border-color: #f0ad4e; 331 | } 332 | 333 | .panel-yellow .panel-heading { 334 | border-color: #f0ad4e; 335 | color: #fff; 336 | background-color: #f0ad4e; 337 | } 338 | 339 | .panel-yellow a { 340 | color: #f0ad4e; 341 | } 342 | 343 | .panel-yellow a:hover { 344 | color: #df8a13; 345 | } -------------------------------------------------------------------------------- /assets/img/avatar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/avatar.png -------------------------------------------------------------------------------- /assets/img/brand-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/brand-logo.png -------------------------------------------------------------------------------- /assets/img/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/favicon.ico -------------------------------------------------------------------------------- /assets/img/flexmail.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/flexmail.png -------------------------------------------------------------------------------- /assets/img/loading.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/loading.gif -------------------------------------------------------------------------------- /assets/img/logo-infoweb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/logo-infoweb.png -------------------------------------------------------------------------------- /assets/img/logo-iw-dark.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/logo-iw-dark.png -------------------------------------------------------------------------------- /assets/img/placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/placeholder.png -------------------------------------------------------------------------------- /assets/img/transparent-placeholder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/infoweb-internet-solutions/yii2-cms/8c41e716e392a699e60af490efb3b4095301eac5/assets/img/transparent-placeholder.png -------------------------------------------------------------------------------- /assets/js/cms.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory; 6 | } else { 7 | root.CMS = factory(); 8 | } 9 | })(this, function () { 10 | 11 | 'use strict'; 12 | 13 | var CMS = { 14 | 'widgets': [], 15 | 'helpers': [] 16 | }; 17 | 18 | /** 19 | * Used in the ckeditor to define hyperlinks 20 | */ 21 | var ckeditorEntitylinkConfiguration = null; 22 | 23 | /** 24 | * Keeps track of every slugify field (true | false). 25 | */ 26 | var allowSlugifyAttribute = {}; 27 | 28 | /** 29 | * Initializes the module: 30 | * - Set global eventhandlers 31 | * 32 | * @return void 33 | */ 34 | CMS.init = function() { 35 | 36 | // Create the sidebar widget 37 | CMS.widgets.sidebar = new sidebar(); 38 | 39 | // Register helpers 40 | CkeditorHelper.register(); 41 | 42 | // Remove eventhandlers that are bound by the kartik sidenav plugin 43 | $('.kv-toggle').unbind('click'); 44 | 45 | // Add scrollbar to side menu 46 | $('.sidebar .kv-sidenav').height(window.innerHeight - 90 - 40).perfectScrollbar(); 47 | 48 | // Toggle bootstrap tooltip 49 | $("[data-toggle='tooltip']").tooltip({ 50 | container: 'body' 51 | }); 52 | 53 | // Toggle bootstrap popover 54 | //$("[data-toggle='popover']").popover(); 55 | 56 | // Set global eventhandlers 57 | $(document) 58 | .on('click', '.navbar-minimalize', CMS.toggleSidebar) 59 | .on('afterValidate', '.tabbed-form', CMS.showFirstFormTabWithErrors) 60 | .on('click', '[id^=grid-pjax] [data-toggleable]', CMS.pjaxGridItemToggle) 61 | .on('keydown', '[data-slugable=true]', CMS.checkAllowSlugifyAttribute) 62 | .on('keyup change', '[data-slugable=true]', CMS.slugifyAttribute) 63 | .on('keydown', '[data-slugified=true]', CMS.validateSlug) 64 | .on('pjax:complete', CMS.pjaxComplete) 65 | .on('mouseover mouseout', '.mini-navbar .sidebar-nav li', function(e) { 66 | $(this).has('.nav-pills').find('.kv-toggle:first').trigger('click', [e.type]); 67 | }) 68 | .on('click', '.kv-toggle', function(e, originalEventType) { 69 | e.preventDefault(); 70 | 71 | // Triggered by mouseover 72 | /*if (originalEventType === 'mouseover') { 73 | $(this).parent().addClass('active'); 74 | $(this).parent().children('ul').show(); 75 | 76 | // Triggered by mouseout 77 | } else if(originalEventType === 'mouseout') { 78 | $(this).parent().removeClass('active'); 79 | $(this).parent().children('ul').hide(); 80 | 81 | // Default 82 | } else {*/ 83 | $(this).parent().toggleClass('active'); 84 | 85 | if ($(this).parent().hasClass('active')) { 86 | $(this).parent().children('ul').show(); 87 | } else { 88 | $(this).parent().children('ul').hide(); 89 | } 90 | 91 | //} 92 | }) 93 | //.on('pjax:complete', '#grid-pjax', CMS.initSortable); 94 | 95 | // Trigger validation if a tabbed form is loaded 96 | if ($('.tabbed-form').length) { 97 | $('.tabbed-form').trigger('afterValidate'); 98 | } 99 | 100 | // Recalculate slug 101 | if($('[data-slugable=true]').length) { 102 | $('[data-slugable=true]').trigger('change'); 103 | } 104 | 105 | // Init the duplicateable jquery plugin 106 | $('[data-duplicateable="true"]').duplicateable(); 107 | }; 108 | 109 | /** 110 | * Refresh sortable 111 | */ 112 | CMS.initSortable = function() { 113 | $('#sortable tbody').sortable('refresh'); 114 | }; 115 | 116 | CMS.toggleSidebar = function(e) { 117 | CMS.widgets.sidebar.toggle(); 118 | }; 119 | 120 | /** 121 | * Shows the first tab on a tabbed form that contains validation errors 122 | * 123 | * @param object Event 124 | * @return void 125 | */ 126 | CMS.showFirstFormTabWithErrors = function(e) { 127 | if ($(".has-error").length) { 128 | var parentTabs = $(".has-error").eq(0).parents(".tab-pane"); 129 | 130 | // Show the last parent tab 131 | $('a[href="#'+parentTabs.last().attr("id")+'"]').tab("show"); 132 | 133 | // If there are multiple parent tabs, show the first one 134 | if (parentTabs.length > 1) 135 | $('a[href="#'+parentTabs.first().attr("id")+'"]').tab("show"); 136 | 137 | CMS.scrollToElement('.has-error'); 138 | } 139 | }; 140 | 141 | /** 142 | * Toggles the state of a 'toggleable' item in a PJAX grid 143 | * 144 | * @param object Event 145 | * @return void 146 | */ 147 | CMS.pjaxGridItemToggle = function(e) { 148 | e.preventDefault(); 149 | 150 | var category = $(this).data('category'); 151 | 152 | var action = $(this).attr('href'), 153 | id = $(this).data('toggle-id'), 154 | request = $.post(action, {id:id}); 155 | 156 | request.done(function(response) { 157 | // Succes, reload PJAX grid 158 | if (response == 1) { 159 | $.pjax.reload({container: '#grid-pjax' + ((category) ? '-' + category : '')}); 160 | } else { 161 | // @todo: catch error 162 | return false; 163 | } 164 | }); 165 | }; 166 | 167 | /** 168 | * Check if we need to redo a slugify. 169 | * 170 | * @param object Event 171 | * @returns void 172 | */ 173 | CMS.checkAllowSlugifyAttribute = function(e) { 174 | var targetId = $(this).data('slug-target').toLowerCase(), 175 | targetElement = $(targetId); 176 | 177 | //allowSlugifyAttribute[targetId] = (targetElement.val() == I18N.slugify($(this).val())) ? true : false; 178 | allowSlugifyAttribute[targetId] = true; 179 | }; 180 | 181 | /** 182 | * Slugifies an attribute and uses it as the value for an other attribute 183 | * 184 | * @param object Event 185 | * @return void 186 | */ 187 | CMS.slugifyAttribute = function(e) { 188 | var targetId = $(this).data('slug-target').toLowerCase(), 189 | targetElement = $(targetId), 190 | targetPlaceholder = targetElement.prop('placeholder'), 191 | slug = I18N.slugify($(this).val()); 192 | 193 | if(typeof allowSlugifyAttribute[targetId] !== "undefined" && allowSlugifyAttribute[targetId]) { 194 | targetElement.val(targetPlaceholder + slug); 195 | } 196 | 197 | window.current_slug = slug; 198 | }; 199 | 200 | CMS.validateSlug = function(e) { 201 | var el = $(this), 202 | placeholder = el.prop('placeholder'), 203 | value = el.val(), 204 | keyCode = e.keyCode || e.which; 205 | 206 | return true; 207 | }; 208 | 209 | /** 210 | * Scrolls to an element with a given speed 211 | * 212 | * @param string The selector 213 | * @return void 214 | */ 215 | CMS.scrollToElement = function(selector, speed) { 216 | // Select the element (limit to the first) 217 | var el = $(selector)[0], 218 | speed = speed || 250; 219 | 220 | // Check for the existance of the element 221 | if($(el).length == 0) 222 | return false; 223 | 224 | // Scroll 225 | $('html,body').animate({scrollTop: $(el).offset().top - 91}, speed); 226 | }; 227 | 228 | /** 229 | * Performs actions when a pjax request is completed 230 | * 231 | * @param Event 232 | * @return void 233 | */ 234 | CMS.pjaxComplete = function(e) { 235 | // Remove tooltips that are left behind in the DOM 236 | $('.tooltip').remove(); 237 | 238 | // Re-initializes tooltips 239 | $('[data-toggle]').tooltip(); 240 | }; 241 | 242 | /** 243 | * Resizes an iframe so that it takes the dimensions of its content 244 | * 245 | * @param object The iframe 246 | * @param int An amount of pixels that will be added to the height as an adjustment 247 | * @return void 248 | */ 249 | CMS.autoSizeIframe = function(obj, adjustment) { 250 | var adjustment = adjustment || 0; 251 | 252 | obj.style.height = obj.contentWindow.document.body.scrollHeight + parseInt(adjustment) + 'px'; 253 | }; 254 | 255 | CMS.addLoaderClass = function(obj) { 256 | var h = (Math.max(document.documentElement.clientHeight, window.innerHeight || 0) - 91) / 2; 257 | 258 | obj.addClass('element-loading'); 259 | obj.css('background-position', 'center '+h+'px'); 260 | }; 261 | 262 | CMS.removeLoaderClass = function(obj) { 263 | obj.removeClass('element-loading'); 264 | }; 265 | 266 | CMS.setCkeditorEntitylinkConfiguration = function(configuration) { 267 | ckeditorEntitylinkConfiguration = configuration; 268 | }; 269 | 270 | CMS.getCkeditorEntitylinkConfiguration = function() { 271 | return ckeditorEntitylinkConfiguration; 272 | } 273 | 274 | // Sidebar widget 275 | var sidebar = function() { 276 | this.state = 'open'; 277 | }; 278 | 279 | /** 280 | * Saves the state of the sidebar in a cookie 281 | * 282 | * @param string The state of the sidebar (open|closed) 283 | * @return void 284 | */ 285 | sidebar.prototype.setStateCookie = function(state) { 286 | var state = state || 'open'; 287 | Cookies.set('infoweb-admin-sidebar-state', state, { expires: Infinity }); 288 | }; 289 | 290 | /** 291 | * Toggles the sidebar by adding a custom class to the body element. 292 | * The state is saved in a cookie 293 | * 294 | * @return void 295 | */ 296 | sidebar.prototype.toggle = function() { 297 | $('body').toggleClass('mini-navbar'); 298 | 299 | var state = ($('body').hasClass('mini-navbar')) ? 'closed' : 'open'; 300 | 301 | if (state == 'closed') { 302 | //$('.nav-pills').hide(); 303 | } 304 | 305 | this.setStateCookie(state); 306 | this.state = state; 307 | }; 308 | 309 | // CKEditor helper 310 | var CkeditorHelper = {}; 311 | 312 | /** 313 | * Registers the plugin in the CMS module 314 | */ 315 | CkeditorHelper.register = function() { 316 | if (typeof CMS !== 'undefined') { 317 | CMS.helpers.CkeditorHelper = CkeditorHelper; 318 | } 319 | 320 | if (typeof CKEDITOR !== 'undefined') { 321 | CkeditorHelper.setElementFilters(); 322 | } 323 | }; 324 | 325 | /** 326 | * Sets filters on html elements 327 | */ 328 | CkeditorHelper.setElementFilters = function() { 329 | CKEDITOR.on('instanceReady', function (ev) { 330 | ev.editor.dataProcessor.htmlFilter.addRules( { 331 | elements : { 332 | img: CkeditorHelper.filterImgElement 333 | } 334 | }); 335 | }); 336 | }; 337 | 338 | /** 339 | * Filters the html of an img element 340 | * 341 | * @param object The image element 342 | */ 343 | CkeditorHelper.filterImgElement = function( el ) { 344 | // Add bootstrap "img-responsive" class to each inserted image 345 | el.addClass('img-responsive'); 346 | 347 | // Remove inline "height" and "width" styles and 348 | // replace them with their attribute counterparts. 349 | // This ensures that the 'img-responsive' class works 350 | var style = el.attributes.style; 351 | 352 | if (style) { 353 | // Get the width from the style. 354 | var match = /(?:^|\s)width\s*:\s*(\d+)px/i.exec(style), 355 | width = match && match[1]; 356 | 357 | // Get the height from the style. 358 | match = /(?:^|\s)height\s*:\s*(\d+)px/i.exec(style); 359 | var height = match && match[1]; 360 | 361 | // Replace the width 362 | if (width) { 363 | el.attributes.style = el.attributes.style.replace(/(?:^|\s)width\s*:\s*(\d+)px;?/i, ''); 364 | el.attributes.width = width; 365 | } 366 | 367 | // Replace the height 368 | if (height) { 369 | el.attributes.style = el.attributes.style.replace(/(?:^|\s)height\s*:\s*(\d+)px;?/i, ''); 370 | el.attributes.height = height; 371 | } 372 | } 373 | 374 | // Remove the style tag if it is empty 375 | if (!el.attributes.style) 376 | delete el.attributes.style; 377 | }; 378 | 379 | return CMS; 380 | }); -------------------------------------------------------------------------------- /assets/js/i18n.js: -------------------------------------------------------------------------------- 1 | (function (root, factory) { 2 | if (typeof define === 'function' && define.amd) { 3 | define(factory); 4 | } else if (typeof exports === 'object') { 5 | module.exports = factory; 6 | } else { 7 | root.I18N = factory(); 8 | } 9 | })(this, function () { 10 | 11 | 'use strict'; 12 | 13 | var I18N = {}; 14 | 15 | // Character maps 16 | var LATIN_MAP = { 17 | 'À': 'A', 'Á': 'A', 'Â': 'A', 'Ã': 'A', 'Ä': 'A', 'Å': 'A', 'Æ': 'AE', 'Ç': 18 | 'C', 'È': 'E', 'É': 'E', 'Ê': 'E', 'Ë': 'E', 'Ì': 'I', 'Í': 'I', 'Î': 'I', 19 | 'Ï': 'I', 'Ð': 'D', 'Ñ': 'N', 'Ò': 'O', 'Ó': 'O', 'Ô': 'O', 'Õ': 'O', 'Ö': 20 | 'O', 'Ő': 'O', 'Ø': 'O', 'Ù': 'U', 'Ú': 'U', 'Û': 'U', 'Ü': 'U', 'Ű': 'U', 21 | 'Ý': 'Y', 'Þ': 'TH', 'Ÿ': 'Y', 'ß': 'ss', 'à':'a', 'á':'a', 'â': 'a', 'ã': 22 | 'a', 'ä': 'a', 'å': 'a', 'æ': 'ae', 'ç': 'c', 'è': 'e', 'é': 'e', 'ê': 'e', 23 | 'ë': 'e', 'ì': 'i', 'í': 'i', 'î': 'i', 'ï': 'i', 'ð': 'd', 'ñ': 'n', 'ò': 24 | 'o', 'ó': 'o', 'ô': 'o', 'õ': 'o', 'ö': 'o', 'ő': 'o', 'ø': 'o', 'ù': 'u', 25 | 'ú': 'u', 'û': 'u', 'ü': 'u', 'ű': 'u', 'ý': 'y', 'þ': 'th', 'ÿ': 'y' 26 | }, 27 | LATIN_SYMBOLS_MAP = { 28 | '©':'(c)' 29 | }, 30 | GREEK_MAP = { 31 | 'α':'a', 'β':'b', 'γ':'g', 'δ':'d', 'ε':'e', 'ζ':'z', 'η':'h', 'θ':'8', 32 | 'ι':'i', 'κ':'k', 'λ':'l', 'μ':'m', 'ν':'n', 'ξ':'3', 'ο':'o', 'π':'p', 33 | 'ρ':'r', 'σ':'s', 'τ':'t', 'υ':'y', 'φ':'f', 'χ':'x', 'ψ':'ps', 'ω':'w', 34 | 'ά':'a', 'έ':'e', 'ί':'i', 'ό':'o', 'ύ':'y', 'ή':'h', 'ώ':'w', 'ς':'s', 35 | 'ϊ':'i', 'ΰ':'y', 'ϋ':'y', 'ΐ':'i', 36 | 'Α':'A', 'Β':'B', 'Γ':'G', 'Δ':'D', 'Ε':'E', 'Ζ':'Z', 'Η':'H', 'Θ':'8', 37 | 'Ι':'I', 'Κ':'K', 'Λ':'L', 'Μ':'M', 'Ν':'N', 'Ξ':'3', 'Ο':'O', 'Π':'P', 38 | 'Ρ':'R', 'Σ':'S', 'Τ':'T', 'Υ':'Y', 'Φ':'F', 'Χ':'X', 'Ψ':'PS', 'Ω':'W', 39 | 'Ά':'A', 'Έ':'E', 'Ί':'I', 'Ό':'O', 'Ύ':'Y', 'Ή':'H', 'Ώ':'W', 'Ϊ':'I', 40 | 'Ϋ':'Y' 41 | }, 42 | TURKISH_MAP = { 43 | 'ş':'s', 'Ş':'S', 'ı':'i', 'İ':'I', 'ç':'c', 'Ç':'C', 'ü':'u', 'Ü':'U', 44 | 'ö':'o', 'Ö':'O', 'ğ':'g', 'Ğ':'G' 45 | }, 46 | RUSSIAN_MAP = { 47 | 'а':'a', 'б':'b', 'в':'v', 'г':'g', 'д':'d', 'е':'e', 'ё':'yo', 'ж':'zh', 48 | 'з':'z', 'и':'i', 'й':'j', 'к':'k', 'л':'l', 'м':'m', 'н':'n', 'о':'o', 49 | 'п':'p', 'р':'r', 'с':'s', 'т':'t', 'у':'u', 'ф':'f', 'х':'h', 'ц':'c', 50 | 'ч':'ch', 'ш':'sh', 'щ':'sh', 'ъ':'', 'ы':'y', 'ь':'', 'э':'e', 'ю':'yu', 51 | 'я':'ya', 52 | 'А':'A', 'Б':'B', 'В':'V', 'Г':'G', 'Д':'D', 'Е':'E', 'Ё':'Yo', 'Ж':'Zh', 53 | 'З':'Z', 'И':'I', 'Й':'J', 'К':'K', 'Л':'L', 'М':'M', 'Н':'N', 'О':'O', 54 | 'П':'P', 'Р':'R', 'С':'S', 'Т':'T', 'У':'U', 'Ф':'F', 'Х':'H', 'Ц':'C', 55 | 'Ч':'Ch', 'Ш':'Sh', 'Щ':'Sh', 'Ъ':'', 'Ы':'Y', 'Ь':'', 'Э':'E', 'Ю':'Yu', 56 | 'Я':'Ya' 57 | }, 58 | UKRAINIAN_MAP = { 59 | 'Є':'Ye', 'І':'I', 'Ї':'Yi', 'Ґ':'G', 'є':'ye', 'і':'i', 'ї':'yi', 'ґ':'g' 60 | }, 61 | CZECH_MAP = { 62 | 'č':'c', 'ď':'d', 'ě':'e', 'ň': 'n', 'ř':'r', 'š':'s', 'ť':'t', 'ů':'u', 63 | 'ž':'z', 'Č':'C', 'Ď':'D', 'Ě':'E', 'Ň': 'N', 'Ř':'R', 'Š':'S', 'Ť':'T', 64 | 'Ů':'U', 'Ž':'Z' 65 | }, 66 | POLISH_MAP = { 67 | 'ą':'a', 'ć':'c', 'ę':'e', 'ł':'l', 'ń':'n', 'ó':'o', 'ś':'s', 'ź':'z', 68 | 'ż':'z', 'Ą':'A', 'Ć':'C', 'Ę':'E', 'Ł':'L', 'Ń':'N', 'Ó':'O', 'Ś':'S', 69 | 'Ź':'Z', 'Ż':'Z' 70 | }, 71 | LATVIAN_MAP = { 72 | 'ā':'a', 'č':'c', 'ē':'e', 'ģ':'g', 'ī':'i', 'ķ':'k', 'ļ':'l', 'ņ':'n', 73 | 'š':'s', 'ū':'u', 'ž':'z', 'Ā':'A', 'Č':'C', 'Ē':'E', 'Ģ':'G', 'Ī':'I', 74 | 'Ķ':'K', 'Ļ':'L', 'Ņ':'N', 'Š':'S', 'Ū':'U', 'Ž':'Z' 75 | }, 76 | ARABIC_MAP = { 77 | 'أ':'a', 'ب':'b', 'ت':'t', 'ث': 'th', 'ج':'g', 'ح':'h', 'خ':'kh', 'د':'d', 78 | 'ذ':'th', 'ر':'r', 'ز':'z', 'س':'s', 'ش':'sh', 'ص':'s', 'ض':'d', 'ط':'t', 79 | 'ظ':'th', 'ع':'aa', 'غ':'gh', 'ف':'f', 'ق':'k', 'ك':'k', 'ل':'l', 'م':'m', 80 | 'ن':'n', 'ه':'h', 'و':'o', 'ي':'y' 81 | }, 82 | LITHUANIAN_MAP = { 83 | 'ą':'a', 'č':'c', 'ę':'e', 'ė':'e', 'į':'i', 'š':'s', 'ų':'u', 'ū':'u', 84 | 'ž':'z', 85 | 'Ą':'A', 'Č':'C', 'Ę':'E', 'Ė':'E', 'Į':'I', 'Š':'S', 'Ų':'U', 'Ū':'U', 86 | 'Ž':'Z' 87 | }, 88 | SERBIAN_MAP = { 89 | 'ђ':'dj', 'ј':'j', 'љ':'lj', 'њ':'nj', 'ћ':'c', 'џ':'dz', 'đ':'dj', 90 | 'Ђ':'Dj', 'Ј':'j', 'Љ':'Lj', 'Њ':'Nj', 'Ћ':'C', 'Џ':'Dz', 'Đ':'Dj' 91 | }, 92 | AZERBAIJANI_MAP = { 93 | 'ç':'c', 'ə':'e', 'ğ':'g', 'ı':'i', 'ö':'o', 'ş':'s', 'ü':'u', 94 | 'Ç':'C', 'Ə':'E', 'Ğ':'G', 'İ':'I', 'Ö':'O', 'Ş':'S', 'Ü':'U' 95 | }, 96 | ALL_MAPS = [ 97 | LATIN_MAP, 98 | LATIN_SYMBOLS_MAP, 99 | GREEK_MAP, 100 | TURKISH_MAP, 101 | RUSSIAN_MAP, 102 | UKRAINIAN_MAP, 103 | CZECH_MAP, 104 | POLISH_MAP, 105 | LATVIAN_MAP, 106 | ARABIC_MAP, 107 | LITHUANIAN_MAP, 108 | SERBIAN_MAP, 109 | AZERBAIJANI_MAP 110 | ], 111 | removeList = []; 112 | 113 | 114 | var Downcoder = { 115 | Initialize: function() { 116 | if (Downcoder.map) { 117 | return; 118 | } 119 | Downcoder.map = {}; 120 | Downcoder.chars = []; 121 | for (var i=0; i') 52 | .attr('href', '#') 53 | .attr('data-duplicateable-language', icon.attr('data-language')) 54 | .attr('data-pjax', 0) 55 | .addClass('duplicateable-all-btn') 56 | .html(icon), 57 | container = $('
') 58 | .addClass('duplicateable-all-container') 59 | .html(btn); 60 | 61 | $(this).replaceWith(container); 62 | 63 | // Bind the click event to the button 64 | btn.on('click', plugin.showDuplicateableModal); 65 | }); 66 | }, 67 | /** 68 | * Inserts a duplication button before the element 69 | * 70 | * @return void 71 | */ 72 | createDuplicationButton: function() { 73 | 74 | // Split the name of the element into pieces 75 | // A valid element should have a name structure of modelName[language][attributeName] 76 | var parts = $(this.element).attr('name').match(/([\w\d]+)\[([-\w]+)\]\[([-\w]+)\]/); 77 | 78 | // Dont create the button if the element name does not matches the pattern 79 | if (!parts || !parts.length || parts.length < 4) 80 | return false; 81 | 82 | var icon = $('') 83 | .addClass('fa') 84 | .addClass('fa-exchange'), 85 | btn = $('') 86 | .attr('href', '#') 87 | .attr('data-duplicateable-model', parts[1]) 88 | .attr('data-duplicateable-language', parts[2]) 89 | .attr('data-duplicateable-attribute', parts[3]) 90 | .addClass('duplicateable-btn') 91 | .html(icon), 92 | container = $('
') 93 | .addClass('duplicateable-container') 94 | .html(btn); 95 | 96 | // Specific code to insert button for bootstrap switch 97 | var bootstrapSwitch = $(this.element).attr('data-krajee-bootstrapSwitch'), 98 | isBootstrapSwitch = typeof bootstrapSwitch !== typeof undefined && bootstrapSwitch !== false; 99 | 100 | // Check parents element 101 | var eq = (isBootstrapSwitch) ? 1 : 0; 102 | 103 | // Insert the btn container after the label that belongs to the element 104 | $(this.element) 105 | .parents('.form-group') 106 | .eq(eq) 107 | .find('label') 108 | .eq(0) 109 | .after(container); 110 | 111 | // Bind the click event to the button 112 | btn.on('click', this.showDuplicateableModal); 113 | }, 114 | showDuplicateableModal: function(e) { 115 | // Collect data attributes of the pushed btn 116 | var modal = $('#duplicateable-modal'), 117 | duplicateableData = { 118 | scope: 'all', 119 | language: $(this).attr('data-duplicateable-language') 120 | }; 121 | 122 | // Collect extra data attributes 123 | if ($(this).hasClass('duplicateable-btn')) { 124 | duplicateableData.scope = 'element'; 125 | duplicateableData.model = $(this).attr('data-duplicateable-model'); 126 | duplicateableData.attribute = $(this).attr('data-duplicateable-attribute'); 127 | } 128 | 129 | // Set the data attributes of the modal 130 | $.each(duplicateableData, function(i) { 131 | modal.data('duplicateable.'+i, this); 132 | }); 133 | 134 | // Reset the checkboxes in the modal: 135 | // - Re-enable and check all languages 136 | // - Disable and uncheck the language of the clicked toggler 137 | // - Uncheck the "duplicate empty values" setting 138 | // - Check the "overwrite values" setting 139 | modal.find('.duplicateable-languages :checkbox').prop('checked', true).prop('disabled', false); 140 | modal.find('.duplicateable-languages :checkbox[value="'+duplicateableData.language+'"]').prop('checked', false).prop('disabled', true); 141 | modal.find('.duplicate-empty-values').prop('checked', false); 142 | modal.find('.overwrite-values').prop('checked', true); 143 | modal.modal('show'); 144 | }, 145 | /** 146 | * Creates a settings object, based on the state of the duplicateable modal 147 | * 148 | * @return object 149 | */ 150 | extractSettingsFromModal: function() { 151 | // Create the settings object 152 | var settings = { 153 | modal: $('#duplicateable-modal'), 154 | duplicateEmptyValues: $('.duplicate-empty-values').is(':checked'), 155 | overwriteValues: $('.overwrite-values').is(':checked'), 156 | }; 157 | settings.scope = settings.modal.data('duplicateable.scope'); 158 | settings.language = settings.modal.data('duplicateable.language'); 159 | settings.selectedLanguages = settings.modal.find('.duplicateable-languages:checked').map(function() { 160 | return this.value; 161 | }).get(); 162 | 163 | return settings; 164 | }, 165 | /** 166 | * Duplicates content 167 | * 168 | * @param {Event} e 169 | */ 170 | duplicate: function(e) { 171 | e.preventDefault(); 172 | // Create the settings object 173 | var settings = Plugin.prototype.extractSettingsFromModal(); 174 | 175 | // Duplicate single element 176 | if (settings.scope == 'element') { 177 | 178 | settings.model = settings.modal.data('duplicateable.model'); 179 | settings.attribute = settings.modal.data('duplicateable.attribute'); 180 | // The original element 181 | var element = $('[name="'+settings.model+'['+settings.language+']['+settings.attribute+']"]'); 182 | 183 | Plugin.prototype.duplicateElement(element, settings).done(function() { 184 | // Close the modal 185 | $('#duplicateable-modal').modal('hide'); 186 | }); 187 | 188 | // Duplicate all elements 189 | } else { 190 | var promises = [], 191 | elements = $('.tab-pane.active .duplicateable-btn[data-duplicateable-language="'+settings.language+'"]'); 192 | 193 | $.each(elements, function() { 194 | var dfd = $.Deferred(); 195 | settings.model = $(this).attr('data-duplicateable-model'); 196 | settings.language = $(this).attr('data-duplicateable-language'); 197 | settings.attribute = $(this).attr('data-duplicateable-attribute'); 198 | // The original element 199 | var element = $('[name="'+settings.model+'['+settings.language+']['+settings.attribute+']"]'); 200 | 201 | Plugin.prototype.duplicateElement(element, settings).done(function() { 202 | dfd.resolve(); 203 | }); 204 | 205 | promises.push(dfd); 206 | }); 207 | 208 | $.when.apply($, promises).done(function() { 209 | // Close the modal 210 | $('#duplicateable-modal').modal('hide'); 211 | }); 212 | } 213 | }, 214 | /** 215 | * Duplicates a single element 216 | * 217 | * @param {jQuery object} element The element who's content has to be duplicated 218 | * @param {object} settings 219 | * @return {Promise} 220 | */ 221 | duplicateElement: function(element, settings) { 222 | var promises = []; 223 | // Extend the settings 224 | settings.value = element.val(), 225 | settings.ckeditorId = false; 226 | settings.bootstrapSwitch = false; 227 | 228 | // The field is a CKeditor 229 | if (element.is('textarea') && element.next().hasClass('cke') && typeof CKEDITOR !== 'undefined') { 230 | settings.ckeditorId = settings.model.toLowerCase() + '-' + settings.language.toLowerCase() + '-' + settings.attribute.toLowerCase(); 231 | settings.value = CKEDITOR.instances[settings.ckeditorId].getData(); 232 | } 233 | 234 | // The field is a Bootstrap Switch 235 | var bootstrapSwitch = $(element).eq(1).attr('data-krajee-bootstrapSwitch'); 236 | if(typeof bootstrapSwitch !== typeof undefined && bootstrapSwitch !== false) { 237 | settings.value = $(this).prop('checked') ? 1 : 0; 238 | settings.ckeditorId = false; 239 | settings.bootstrapSwitch = true; 240 | } 241 | 242 | $.each(settings.selectedLanguages, function() { 243 | var dfd = $.Deferred(); 244 | Plugin.prototype.copyContent(settings, this).done(function() { 245 | dfd.resolve(); 246 | }); 247 | promises.push(dfd); 248 | }); 249 | 250 | return $.when.apply($, promises).promise(); 251 | }, 252 | /** 253 | * Copy content to an element, based on the settings that are passed: 254 | * - model: The model name 255 | * - attribute: The attribute name 256 | * - value: The value that has to be copied 257 | * - ckeditorId: false or the id of the ckeditor instance 258 | * - duplicateEmptyValues: if true, the value has to be copied even 259 | * if it's empty 260 | * - overwriteValues: if true, the value has to be copied even if the 261 | * field is not empty 262 | * 263 | * @param {object} settings 264 | * @param {string} language The language of element that receives the content 265 | * @return {Promise} 266 | */ 267 | copyContent: function(settings, language) { 268 | var dfd = $.Deferred(); 269 | // Duplicate empty values? 270 | if (settings.value !== '' || (settings.value === '' && settings.duplicateEmptyValues)) { 271 | 272 | // CKEditor 273 | if (typeof settings.ckeditorId !== 'undefined' && settings.ckeditorId !== false) { 274 | // The ckeditorId of the original element is provided, so to find 275 | // the id of the element's instance, we have to replace the original 276 | // language with the provided one 277 | var ckeditorId = settings.ckeditorId.replace(settings.language.toLowerCase(), language.toLowerCase()), 278 | currentValue = CKEDITOR.instances[ckeditorId].getData(); 279 | 280 | // Overwrite values? 281 | if (settings.overwriteValues || (!settings.overwriteValues && currentValue === '')) { 282 | CKEDITOR.instances[ckeditorId].setData(settings.value); 283 | } 284 | } 285 | // Bootstrap Switch 286 | else if(settings.bootstrapSwitch == true) { 287 | // Overwrite values? 288 | if (settings.overwriteValues) { 289 | if (settings.value == true) { 290 | $('[type="checkbox"][name="'+settings.model+'['+language+']['+settings.attribute+']"]').bootstrapSwitch('state', true); 291 | } 292 | else { 293 | $('[type="checkbox"][name="'+settings.model+'['+language+']['+settings.attribute+']"]').bootstrapSwitch('state', true); 294 | } 295 | 296 | } 297 | } 298 | // Input field 299 | else { 300 | var currentValue = $('[name="'+settings.model+'['+language+']['+settings.attribute+']"]').val(); 301 | 302 | // Overwrite values? 303 | if (settings.overwriteValues || (!settings.overwriteValues && currentValue === '')) { 304 | $('[name="'+settings.model+'['+language+']['+settings.attribute+']"]').val(settings.value); 305 | } 306 | } 307 | } 308 | 309 | dfd.resolve(); 310 | return dfd.promise(); 311 | } 312 | }); 313 | 314 | // A really lightweight plugin wrapper around the constructor, 315 | // preventing against multiple instantiations 316 | $.fn[ pluginName ] = function ( options ) { 317 | return this.each(function() { 318 | if ( !$.data( this, "plugin_" + pluginName ) ) { 319 | $.data( this, "plugin_" + pluginName, new Plugin( this, options ) ); 320 | } 321 | }); 322 | }; 323 | 324 | // Bind event to the duplication btn 325 | $('#duplicateable-modal #do-duplication').on('click', Plugin.prototype.duplicate); 326 | 327 | })( jQuery, window, document ); -------------------------------------------------------------------------------- /assets/js/main.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Overwrite default delete confirm dialog 3 | * 4 | * @param message 5 | * @param ok 6 | * @param cancel 7 | * @returns {boolean} 8 | */ 9 | yii.confirm = function (message, ok, cancel) { 10 | bootbox.confirm(message, function (confirmed) { 11 | if (confirmed) { 12 | !ok || ok(); 13 | } else { 14 | !cancel || cancel(); 15 | } 16 | }); 17 | return false; 18 | }; 19 | 20 | $(function() { 21 | // Init CMS module 22 | CMS.init(); 23 | }); 24 | -------------------------------------------------------------------------------- /assets/js/media.js: -------------------------------------------------------------------------------- 1 | $(function() { 2 | 3 | moxman.browse({ 4 | insert: false, 5 | close: false, 6 | remember_last_path: true, 7 | view: 'thumbs', 8 | fullscreen: true 9 | //path: '/uploads/img' 10 | }); 11 | 12 | /* 13 | // Disable esc key 14 | $(document).keyup(function(e) { 15 | if (e.keyCode == 27) { 16 | e.preventDefault(); 17 | e.stopPropagation(); 18 | console.log('test'); 19 | return false; 20 | } 21 | }); 22 | */ 23 | }); -------------------------------------------------------------------------------- /behaviors/Base64EncodeBehavior.php: -------------------------------------------------------------------------------- 1 | 'encode', 15 | ActiveRecord::EVENT_BEFORE_UPDATE => 'encode', 16 | ActiveRecord::EVENT_AFTER_INSERT => 'decode', 17 | ActiveRecord::EVENT_AFTER_UPDATE => 'decode', 18 | ActiveRecord::EVENT_AFTER_FIND => 'decode', 19 | ]; 20 | } 21 | 22 | public function encode($event) 23 | { 24 | foreach ($this->attributes as $attribute) { 25 | $this->owner->$attribute = base64_encode($this->owner->$attribute); 26 | } 27 | } 28 | 29 | public function decode($event) 30 | { 31 | foreach ($this->attributes as $attribute) { 32 | $this->owner->$attribute = base64_decode($this->owner->$attribute); 33 | } 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /behaviors/FileBehave.php: -------------------------------------------------------------------------------- 1 | owner; 20 | $model->file = UploadedFile::getInstance($model, 'file'); 21 | 22 | if ($model->validate() && $model->file) { 23 | 24 | $fileName = Inflector::slug($model->file->baseName) . '.' . $model->file->extension; 25 | $folder = $this->getModelSubDir() . '/'; 26 | $model->path = $fileName; 27 | 28 | BaseFileHelper::removeDirectory(Yii::getAlias('@uploadsBasePath') . "/files/{$folder}"); 29 | 30 | if (!BaseFileHelper::createDirectory(Yii::getAlias('@uploadsBasePath') . "/files/{$folder}")) { 31 | throw new Exception(Yii::t('app', 'Failed to create the file upload directory')); 32 | } 33 | 34 | $model->file->saveAs(Yii::getAlias('@uploadsBasePath') . "/files/{$folder}{$model->path}"); 35 | 36 | 37 | $model->save(); 38 | } 39 | 40 | return true; 41 | 42 | } catch(yii\base\Exception $e) { 43 | return $e->getMessage(); 44 | } 45 | } 46 | 47 | /** 48 | * @inheritdoc 49 | */ 50 | public function rules() 51 | { 52 | return [ 53 | [['file'], 'safe'], 54 | [['file'], 'file', 'skipOnEmpty' => true] 55 | ]; 56 | } 57 | 58 | 59 | public function getModelSubDir() 60 | { 61 | $modelName = $this->getShortClass($this->owner); 62 | $modelDir = Inflector::pluralize($modelName).'/'. $modelName . $this->owner->id; 63 | 64 | return $modelDir; 65 | } 66 | 67 | public function getShortClass($obj) 68 | { 69 | $className = get_class($obj); 70 | 71 | if (preg_match('@\\\\([\w]+)$@', $className, $matches)) { 72 | $className = $matches[1]; 73 | } 74 | 75 | return $className; 76 | } 77 | 78 | public function getHint() { 79 | 80 | if ($this->owner->isNewRecord || !$this->file) { 81 | return ''; 82 | } 83 | 84 | return Html::a( 85 | Yii::t('app', 'View attached file'), 86 | $this->fileUrl, 87 | ['target' => '_blank'] 88 | ); 89 | } 90 | 91 | public function getFileUrl() { 92 | return Yii::getAlias('@uploadsBaseUrl') . "/files/{$this->getModelSubDir()}/{$this->owner->path}"; 93 | } 94 | } -------------------------------------------------------------------------------- /behaviors/HomepageBehavior.php: -------------------------------------------------------------------------------- 1 | 'checkValue', 20 | ActiveRecord::EVENT_BEFORE_UPDATE => 'checkValue' 21 | ]; 22 | } 23 | 24 | public function checkValue($event) 25 | { 26 | $node = $this->owner; 27 | 28 | // If the item is set as the homepage and the maximum allowed amount is 29 | // 1, change the value of this attribute for all other items 30 | if ($this->maxItems == 1 && $node->show_on_homepage == 1) { 31 | $node::updateAll(['show_on_homepage' => 0]); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /behaviors/ImageBehave.php: -------------------------------------------------------------------------------- 1 | owner->primaryKey) { 38 | throw new \Exception('Owner must have primaryKey when you attach image!'); 39 | } 40 | 41 | $pictureFileName = basename($absolutePath); 42 | $pictureSubDir = $this->getModule()->getModelSubDir($this->owner); 43 | $storePath = $this->getModule()->getStorePath($this->owner); 44 | 45 | $newAbsolutePath = $storePath . 46 | DIRECTORY_SEPARATOR . $pictureSubDir . 47 | DIRECTORY_SEPARATOR . $pictureFileName; 48 | 49 | BaseFileHelper::createDirectory($storePath . DIRECTORY_SEPARATOR . $pictureSubDir, 50 | 0775, true); 51 | 52 | copy($absolutePath, $newAbsolutePath); 53 | unlink($absolutePath); 54 | 55 | if (!file_exists($newAbsolutePath)) { 56 | throw new \Exception('Cant copy file! ' . $absolutePath . ' to ' . $newAbsolutePath); 57 | } 58 | 59 | $image = new Image; 60 | $image->itemId = $this->owner->primaryKey; 61 | $image->filePath = $pictureSubDir . '/' . $pictureFileName; 62 | $image->modelName = $this->getModule()->getShortClass($this->owner); 63 | $image->urlAlias = $this->getAlias($image); 64 | $image->identifier = $identifier; 65 | $image->name = substr(yii\helpers\Inflector::slug($pictureFileName), 0, -3); 66 | 67 | // Get the highest position 68 | // @todo Create function 69 | $owner = $this->owner; 70 | $query = (new yii\db\Query())->select('MAX(`position`)')->from(Image::tableName())->where(['modelName' => yii\helpers\StringHelper::basename($owner::className())]); 71 | $command = $query->createCommand(); 72 | $image->position = $command->queryOne(\PDO::FETCH_COLUMN)+1; 73 | 74 | if(!$image->save()){ 75 | return false; 76 | } 77 | 78 | // Add the translations 79 | foreach (Yii::$app->params['languages'] as $language => $data) { 80 | $imageLang = new ImageLang(); 81 | $imageLang->language = $language; 82 | $image->link('translations', $imageLang); 83 | } 84 | 85 | if (count($image->getErrors()) > 0) { 86 | 87 | $ar = array_shift($image->getErrors()); 88 | 89 | unlink($newAbsolutePath); 90 | throw new \Exception(array_shift($ar)); 91 | } 92 | $img = $this->owner->getImage(); 93 | 94 | $this->setMainImage($image); 95 | 96 | return $image; 97 | } 98 | 99 | /** 100 | * Sets main image of model 101 | * @param $img 102 | * @throws \Exception 103 | */ 104 | public function setMainImage($img) 105 | { 106 | if ($this->owner->primaryKey != $img->itemId) { 107 | throw new \Exception('Image must belong to this model'); 108 | } 109 | 110 | $where = ['itemId' => $this->owner->id, 'modelName' => StringHelper::basename($this->owner->className())]; 111 | 112 | // Check if the main image is already set 113 | $mainImage = $img->findOne(yii\helpers\ArrayHelper::merge(['isMain' => 1], $where)); 114 | 115 | if ($mainImage) { 116 | return false; 117 | } 118 | 119 | // Reset main image 120 | $img->updateAll(['isMain' => 0], $where); 121 | 122 | // Clear images cache 123 | $this->owner->clearImagesCache(); 124 | 125 | // Set new main image 126 | $img->setMain(true); 127 | return $img->save(); 128 | } 129 | 130 | /** Make string part of image's url 131 | * @return string 132 | * @throws \Exception 133 | */ 134 | private function getAliasString() 135 | { 136 | if ($this->createAliasMethod) { 137 | $string = $this->owner->{$this->createAliasMethod}(); 138 | if (!is_string($string)) { 139 | throw new \Exception(Yii::t('app', 'Invalid image alias')); 140 | } else { 141 | return $string; 142 | } 143 | 144 | } else { 145 | return substr(md5(microtime()), 0, 10); 146 | } 147 | } 148 | 149 | /** 150 | * Returns model images 151 | * First image alwats must be main image 152 | * @return array|yii\db\ActiveRecord[] 153 | */ 154 | public function getImages($additionWhere = false, $fallbackToPlaceholder = true, $sort = ['position' => SORT_DESC]) 155 | { 156 | $finder = $this->getImagesFinder($additionWhere); 157 | 158 | $imageQuery = Image::find() 159 | ->where($finder); 160 | $imageQuery->orderBy($sort); 161 | $imageRecords = $imageQuery->all(); 162 | 163 | if(!$imageRecords && $fallbackToPlaceholder){ 164 | return [new PlaceHolder]; 165 | } 166 | return $imageRecords; 167 | } 168 | 169 | /** 170 | * Remove all model images 171 | */ 172 | public function removeImages() 173 | { 174 | $images = $this->owner->getImages(); 175 | if (count($images) < 1) { 176 | return true; 177 | } else { 178 | foreach ($images as $image) { 179 | $this->owner->removeImage($image); 180 | } 181 | } 182 | } 183 | 184 | /** 185 | * Returns main model image 186 | * @param boolean $fallbackToPlaceholder A flag to determine if a 187 | * placeholder has to be used 188 | * when no image is found 189 | * @param mixed $placeHolderPath The alternative placeholder path 190 | * @return array|null|ActiveRecord 191 | */ 192 | public function getImage($fallbackToPlaceholder = true, $placeHolderPath = null) 193 | { 194 | $finder = $this->getImagesFinder(/*['isMain' => 1]*/); 195 | $imageQuery = Image::find()->where($finder); 196 | $imageQuery->orderBy(['isMain' => SORT_DESC, 'id' => SORT_ASC]); 197 | $img = $imageQuery->one(); 198 | 199 | // No image model + fallback to placeholder or 200 | // image model but image does not exist + fallback to placeholder 201 | if ((!$img && $fallbackToPlaceholder) || 202 | ($img !== null && !file_exists($img->getBaseUrl()) && $fallbackToPlaceholder)) { 203 | 204 | // Custom placeholder 205 | if ($placeHolderPath) { 206 | $placeHolder = new Image([ 207 | 'filePath' => basename(Yii::getAlias($placeHolderPath)), 208 | 'urlAlias' => basename($placeHolderPath) 209 | ]); 210 | 211 | return $placeHolder; 212 | // Default placeholder 213 | } else { 214 | return new PlaceHolder; 215 | } 216 | } 217 | 218 | return $img; 219 | } 220 | 221 | /** 222 | * returns main model image 223 | * @return array|null|ActiveRecord 224 | */ 225 | /*public function getImage() 226 | { 227 | if ($this->getModule()->className === null) { 228 | $imageQuery = Image::find(); 229 | } else { 230 | $class = $this->getModule()->className; 231 | $imageQuery = $class::find(); 232 | } 233 | $finder = $this->getImagesFinder(['isMain' => 1]); 234 | $imageQuery->where($finder); 235 | $imageQuery->orderBy(['isMain' => SORT_DESC, 'id' => SORT_ASC]); 236 | 237 | $img = $imageQuery->one(); 238 | if(!$img){ 239 | return new PlaceHolder; 240 | } 241 | 242 | return $img; 243 | }*/ 244 | 245 | private function getImagesFinder($additionWhere = false) 246 | { 247 | $base = [ 248 | 'itemId' => $this->owner->id, 249 | 'modelName' => $this->getModule()->getShortClass($this->owner) 250 | ]; 251 | 252 | if ($additionWhere) { 253 | $base = \yii\helpers\BaseArrayHelper::merge($base, $additionWhere); 254 | } 255 | 256 | return $base; 257 | } 258 | 259 | /** 260 | * Returns model images 261 | * First image alwats must be main image 262 | * @return array|yii\db\ActiveRecord[] 263 | */ 264 | public function clearCache() 265 | { 266 | $finder = $this->getImagesFinder(); 267 | 268 | $imageQuery = Image::find() 269 | ->where($finder); 270 | $imageQuery->orderBy(['isMain' => SORT_DESC, 'id' => SORT_ASC]); 271 | 272 | $imageRecords = $imageQuery->all(); 273 | if(!$imageRecords){ 274 | return [new PlaceHolder]; 275 | } 276 | return $imageRecords; 277 | } 278 | 279 | /** 280 | * 281 | * removes concrete model's image 282 | * @param Image $img 283 | * @throws \Exception 284 | */ 285 | public function removeImage(\rico\yii2images\models\Image $img) 286 | { 287 | if (!$img->isNewRecord) { 288 | $imgInfoweb = Image::findOne(['id' => $img->id]); 289 | $imgInfoweb->clearCache(); 290 | 291 | $storePath = $this->getModule()->getStorePath(); 292 | 293 | $fileToRemove = $storePath . DIRECTORY_SEPARATOR . $img->filePath; 294 | if (preg_match('@\.@', $fileToRemove) and is_file($fileToRemove)) { 295 | unlink($fileToRemove); 296 | } 297 | $img->delete(); 298 | } 299 | } 300 | 301 | /** 302 | * Upload and attach images 303 | * 304 | * @param $model 305 | */ 306 | public function uploadImage() { 307 | 308 | // Upload image 309 | $form = new ImageUploadForm(); 310 | $images = UploadedFile::getInstances($form, 'image'); 311 | 312 | $model = $this->owner; 313 | 314 | // Remove old images if a new one is uploaded 315 | if ($images) { 316 | $model->removeImages(); 317 | 318 | foreach ($images as $k => $image) { 319 | 320 | $_model = new ImageUploadForm(); 321 | $_model->image = $image; 322 | 323 | if ($_model->validate()) { 324 | $path = \Yii::getAlias('@uploadsBasePath') . "/img/{$_model->image->baseName}.{$_model->image->extension}"; 325 | 326 | $_model->image->saveAs($path); 327 | 328 | // Attach image to model 329 | $model->attachImage($path); 330 | 331 | } else { 332 | foreach ($_model->getErrors('image') as $error) { 333 | $model->addError('image', $error); 334 | } 335 | } 336 | } 337 | 338 | if ($model->hasErrors('image')){ 339 | $model->addError( 340 | 'image', 341 | count($model->getErrors('image')) . Yii::t('app', 'of') . count($images) . ' ' . Yii::t('app', 'images not uploaded') 342 | ); 343 | } else { 344 | Yii::$app->session->setFlash(StringHelper::basename($this->owner->className()), Yii::t('app', '{n, plural, =1{Image} other{# images}} successfully uploaded', ['n' => count($images)])); 345 | } 346 | } 347 | } 348 | 349 | public function getUploadedImages() 350 | { 351 | return UploadedFile::getInstances(new ImageUploadForm, 'image'); 352 | } 353 | 354 | public function getUploadedImageByName($name) 355 | { 356 | return UploadedFile::getInstanceByName($name); 357 | } 358 | 359 | /** 360 | * Uploads and attaches an image 361 | * 362 | * @param string $name The name if the image in the uploaded files array 363 | * @param boolean $isMain The main image flag 364 | * @param string $identifier The image identifier 365 | */ 366 | public function uploadImageByName($name = '', $isMain = false, $identifier = '') 367 | { 368 | // Load the image 369 | $image = $this->getUploadedImageByName($name); 370 | 371 | if ($image) { 372 | $owner = $this->owner; 373 | 374 | // Delete current image 375 | if (!empty($identifier)) { 376 | $owner->removeImageByIdentifier($identifier); 377 | } else { 378 | $owner->removeImages(); 379 | } 380 | 381 | // Create uploadform and set the image 382 | $upload = new ImageUploadForm; 383 | $upload->image = $image; 384 | 385 | // Validate the upload 386 | if ($upload->validate()) { 387 | // Upload the file 388 | $path = \Yii::getAlias('@uploadsBasePath') . "/img/{$upload->image->baseName}.{$upload->image->extension}"; 389 | $uploaded = $upload->image->saveAs($path); 390 | 391 | // Attach the file to the owner 392 | if ($uploaded) { 393 | $owner->attachImage($path, $isMain, $identifier); 394 | } 395 | } else { 396 | // Add upload errors to the owner 397 | foreach ($upload->getErrors('image') as $error) { 398 | $owner->addError('image', $error); 399 | } 400 | } 401 | } 402 | } 403 | 404 | /** 405 | * Removes an image with a specific identifier 406 | * 407 | * @param string $identifier The identifier of the image 408 | * @return boolean 409 | */ 410 | public function removeImageByIdentifier($identifier = '') 411 | { 412 | if ($identifier == '') { 413 | return $this->owner->removeImages(); 414 | } 415 | 416 | $image = Image::findOne([ 417 | 'identifier' => $identifier, 418 | 'itemId' => $this->owner->id, 419 | 'modelName' => $this->getModule()->getShortClass($this->owner) 420 | ]); 421 | 422 | if ($image) { 423 | $this->owner->removeImage($image); 424 | } 425 | 426 | return true; 427 | } 428 | 429 | /** 430 | * Returns a model's image with a specific identifier 431 | * 432 | * @param string $identifier The identifier of the image 433 | * @return array|null|ActiveRecord 434 | */ 435 | public function getImageByIdentifier($identifier = '', $fallbackToPlaceholder = true, $placeHolderPath = null) 436 | { 437 | if ($identifier == '') { 438 | return $this->owner->getImage($fallbackToPlaceholder, $placeHolderPath); 439 | } 440 | 441 | if ($this->getModule()->className === null) { 442 | $imageQuery = Image::find(); 443 | } else { 444 | $class = $this->getModule()->className; 445 | $imageQuery = $class::find(); 446 | } 447 | $finder = $this->getImagesFinder(['identifier' => $identifier]); 448 | $imageQuery->where($finder); 449 | 450 | $img = $imageQuery->one(); 451 | 452 | if(!$img && $fallbackToPlaceholder){ 453 | 454 | if (!$placeHolderPath) { 455 | return new PlaceHolder; 456 | } else { 457 | return new Image([ 458 | 'filePath' => basename(Yii::getAlias($placeHolderPath)), 459 | 'urlAlias' => basename($placeHolderPath) 460 | ]); 461 | } 462 | } 463 | 464 | return $img; 465 | } 466 | 467 | /** 468 | * 469 | * Обновить алиасы для картинок 470 | * Зачистить кэш 471 | */ 472 | private function getAlias() 473 | { 474 | $aliasWords = $this->getAliasString(); 475 | $imagesCount = count($this->owner->getImages()); 476 | 477 | return $aliasWords . '-' . intval($imagesCount + 1); 478 | } 479 | 480 | /** 481 | * Url for removing images in image widget 482 | * 483 | * @param null $identifier 484 | * @return string 485 | */ 486 | public function getRemoveRequest($identifier = null) 487 | { 488 | $action = Url::to(['/cms/image/remove-image']); 489 | $className = str_replace('\\', '\\\\', $this->owner->className()); 490 | 491 | return "'{$action}', {modelId: '{$this->owner->id}', model: '{$className}', identifier: '{$identifier}'}"; 492 | } 493 | } -------------------------------------------------------------------------------- /behaviors/PdfBehave.php: -------------------------------------------------------------------------------- 1 | owner; 19 | $fileName = Inflector::slug($model->fullAddress) . '.pdf'; 20 | $folder = $this->getModelSubDir() . '/'; 21 | $model->pdf_path = $fileName; 22 | 23 | $pdf = new Pdf([ 24 | 'mode' => Pdf::MODE_UTF8, // leaner size using standard fonts 25 | 'destination' => Pdf::DEST_FILE, 26 | 'content' => Yii::$app->view->render('@frontend/views/site/real-estate/print', ['model' => $model]), 27 | 'methods' => [ 28 | 'SetHeader' => ['Rapport ' . $model->fullAddress], 29 | 'SetFooter' => ['|Pagina {PAGENO}|'], 30 | ], 31 | 'filename' => Yii::getAlias('@uploadsBasePath') . "/files/{$folder}{$fileName}", 32 | ]); 33 | 34 | $this->file = $pdf; 35 | 36 | BaseFileHelper::removeDirectory(Yii::getAlias('@uploadsBasePath') . "/files/{$folder}"); 37 | 38 | if (!BaseFileHelper::createDirectory(Yii::getAlias('@uploadsBasePath') . "/files/{$folder}")) { 39 | throw new Exception(Yii::t('app', 'Failed to create the file upload directory')); 40 | } 41 | 42 | // Save and update model 43 | $pdf->render(); 44 | $model->save(); 45 | 46 | return true; 47 | 48 | } catch(yii\base\Exception $e) { 49 | return $e->getMessage(); 50 | } 51 | } 52 | 53 | /** 54 | * @inheritdoc 55 | */ 56 | public function rules() 57 | { 58 | return []; 59 | } 60 | 61 | 62 | public function getModelSubDir() 63 | { 64 | $modelName = $this->getShortClass($this->owner); 65 | $modelDir = Inflector::pluralize($modelName).'/'. $modelName . $this->owner->id; 66 | 67 | return $modelDir; 68 | } 69 | 70 | public function getShortClass($obj) 71 | { 72 | $className = get_class($obj); 73 | 74 | if (preg_match('@\\\\([\w]+)$@', $className, $matches)) { 75 | $className = $matches[1]; 76 | } 77 | 78 | return $className; 79 | } 80 | 81 | public function getHint() { 82 | 83 | if ($this->owner->isNewRecord) { 84 | return ''; 85 | } 86 | 87 | return Html::a( 88 | Yii::t('app', 'View attached file'), 89 | $this->fileUrl, 90 | ['target' => '_blank'] 91 | ); 92 | } 93 | 94 | public function getFileUrl() { 95 | return Yii::getAlias('@uploadsBaseUrl') . "/files/{$this->getModelSubDir()}/{$this->owner->report_path}"; 96 | } 97 | } -------------------------------------------------------------------------------- /components/Frontend.php: -------------------------------------------------------------------------------- 1 | page = $this->activePage; 25 | $this->menuItem = $this->activeMenuItem; 26 | $this->parentMenuItem = $this->activeParentMenuItem; 27 | $this->parentPage = $this->activeParentPage; 28 | } 29 | 30 | public function setPage($page) 31 | { 32 | $this->page = $page; 33 | $this->menuItem = $this->activeMenuItem; 34 | $this->parentMenuItem = $this->activeParentMenuItem; 35 | $this->parentPage = $this->activeParentPage; 36 | } 37 | 38 | /** 39 | * Returns a page, based on the alias that is provided in the request or, 40 | * if no alias is provided, the homepage 41 | * 42 | * @return Page 43 | */ 44 | public function getActivePage() 45 | { 46 | // An alias is provided 47 | if (Yii::$app->request->get('alias')) { 48 | 49 | // Load the alias translation 50 | $aliasLang = AliasLang::findOne([ 51 | 'url' => Yii::$app->request->get('alias'), 52 | 'language' => Yii::$app->language 53 | ]); 54 | 55 | if (!$aliasLang) { 56 | return Yii::$app->response->redirect('@web/404'); 57 | } 58 | 59 | // Get the alias 60 | $alias = $aliasLang->alias; 61 | 62 | // Get the page 63 | $page = $alias->entityModel; 64 | 65 | // The page must be active 66 | if ($page->active != 1) { 67 | return Yii::$app->response->redirect('@web/404'); 68 | } 69 | 70 | 71 | } else { 72 | // Load the page that is marked as the 'homepage' 73 | $page = Page::findOne(['homepage' => 1]); 74 | } 75 | 76 | return $page; 77 | } 78 | 79 | /** 80 | * Get the active parent page 81 | * 82 | * @return Page 83 | */ 84 | public function getActiveParentPage() 85 | { 86 | if (isset($this->parentMenuItem->entity_id)) { 87 | return Page::findOne(['id' => $this->parentMenuItem->entity_id, 'active' => 1]); 88 | } else { 89 | return new Page; 90 | } 91 | } 92 | 93 | /** 94 | * Get the active menu item 95 | * 96 | * @return null|static 97 | */ 98 | public function getActiveMenuItem() 99 | { 100 | $menuItem = MenuItem::findOne([ 101 | 'entity' => MenuItem::ENTITY_PAGE, 102 | 'entity_id' => $this->page->id, 103 | 'active' => 1, 104 | //'menu_id' => 1, @todo Set correnct menu id? 105 | ]); 106 | 107 | return $menuItem; 108 | } 109 | 110 | /** 111 | * Get the active parent menu item 112 | * 113 | * @return MenuItem 114 | */ 115 | public function getActiveParentMenuItem() 116 | { 117 | $menuItem = $this->menuItem->parent; 118 | 119 | if ($menuItem && $menuItem->active = 1) { 120 | return $menuItem; 121 | } else { 122 | return new MenuItem; 123 | } 124 | } 125 | } -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "infoweb-internet-solutions/yii2-cms", 3 | "description": "CMS module for Yii2", 4 | "keywords": [ 5 | "yii2", 6 | "yii2-cms", 7 | "cms", 8 | "infoweb" 9 | ], 10 | "type": "yii2-extension", 11 | "license": "MIT", 12 | "support": { 13 | "issues": "https://github.com/infoweb-internet-solutions/yii2-cms/issues?state=open", 14 | "source": "https://github.com/infoweb-internet-solutions/yii2-cms" 15 | }, 16 | "authors": [ 17 | { 18 | "name": "Fabio Maggio", 19 | "email": "fabio@infoweb.be", 20 | "homepage": "https://github.com/fabiomaggio" 21 | }, 22 | { 23 | "name": "Ruben Heymans", 24 | "email": "ruben@infoweb.be", 25 | "homepage": "http://www.infoweb.be/" 26 | } 27 | ], 28 | "require": { 29 | "yiisoft/yii2": "@stable", 30 | "infoweb-internet-solutions/yii2-cms-settings": "*", 31 | "infoweb-internet-solutions/yii2-cms-user": "*", 32 | "infoweb-internet-solutions/yii2-cms-pages": "*", 33 | "infoweb-internet-solutions/yii2-cms-partials": "*", 34 | "infoweb-internet-solutions/yii2-cms-seo": "*", 35 | "infoweb-internet-solutions/yii2-cms-menu": "*", 36 | "infoweb-internet-solutions/yii2-cms-alias": "*", 37 | "infoweb-internet-solutions/yii2-cms-analytics": "*", 38 | "infoweb-internet-solutions/yii2-cms-email": "*", 39 | "infoweb-internet-solutions/yii2-sortable-behavior": "*", 40 | "mdmsoft/yii2-admin": "@stable", 41 | "kartik-v/yii2-helpers": "@stable", 42 | "kartik-v/yii2-widgets": "@stable", 43 | "kartik-v/yii2-grid": "@stable", 44 | "kartik-v/bootstrap-fileinput": "@stable", 45 | "kartik-v/yii2-icons": "@stable", 46 | "kartik-v/yii2-datecontrol": "@stable", 47 | "2amigos/yii2-translateable-behavior": "@stable", 48 | "costa-rico/yii2-images": "@stable", 49 | "mihaildev/yii2-ckeditor": "dev-infoweb as @stable", 50 | "zelenin/yii2-i18n-module": "dev-infoweb as @stable", 51 | "bower-asset/bootbox": "@stable", 52 | "bower-asset/fastclick": "@stable", 53 | "bower-asset/js-cookie": "@stable", 54 | "bower-asset/perfect-scrollbar": "@stable", 55 | "bower-asset/underscore": "@stable", 56 | "bower-asset/fancybox": "@stable", 57 | "bower-asset/bootstrap-hover-dropdown": "@stable", 58 | "bower-asset/x-editable": "@stable", 59 | "jlorente/yii2-widget-remainingcharacters": "*", 60 | "codemix/yii2-localeurls": "*", 61 | "fishvision/yii2-migrate" : "*", 62 | "creocoder/yii2-translateable": "~1.0", 63 | "bower-asset/bootstrap": "~3.3" 64 | }, 65 | "autoload": { 66 | "psr-4": { 67 | "infoweb\\cms\\": "" 68 | } 69 | } 70 | } 71 | 72 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | 1, 22 | 'msg' => '' 23 | ]; 24 | 25 | $post = Yii::$app->request->post(); 26 | 27 | if (isset($post['model']) && !empty($post['model'])) { 28 | 29 | /// Load model 30 | $model = Yii::createObject(['class' => $post['model'], 'id' => $post['modelId']]); 31 | 32 | // Remove the image 33 | $model->removeImageByIdentifier($post['identifier']); 34 | } 35 | 36 | // Return validation in JSON format 37 | Yii::$app->response->format = Response::FORMAT_JSON; 38 | return $response; 39 | } 40 | 41 | } 42 | -------------------------------------------------------------------------------- /controllers/MediaController.php: -------------------------------------------------------------------------------- 1 | render('index'); 13 | } 14 | 15 | public function actionMedia() 16 | { 17 | $this->layout = 'media'; 18 | return $this->render('media'); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /controllers/ParseController.php: -------------------------------------------------------------------------------- 1 | controller->module; 22 | $post = Yii::$app->request->post(); 23 | if (isset($post['displayDate'])) { 24 | $type = empty($post['type']) ? Module::FORMAT_DATE : $post['type']; 25 | $saveFormat = ArrayHelper::getValue($post, 'saveFormat'); 26 | $dispFormat = ArrayHelper::getValue($post, 'dispFormat') . '|'; 27 | 28 | // If the date if saved as a unix timestamp and the format type is 29 | // a date, add a special character to ensure that only the date is 30 | // saved without the time 31 | if ($type == Module::FORMAT_DATE && $saveFormat == 'U') 32 | $dispFormat .= '|'; 33 | 34 | $dispTimezone = ArrayHelper::getValue($post, 'dispTimezone'); 35 | $saveTimezone = ArrayHelper::getValue($post, 'saveTimezone'); 36 | if ($dispTimezone != null) { 37 | $date = DateTime::createFromFormat($dispFormat, $post['displayDate'], new DateTimeZone($dispTimezone)); 38 | } else { 39 | $date = DateTime::createFromFormat($dispFormat, $post['displayDate']); 40 | } 41 | if (empty($date) || !$date) { 42 | $value = ''; 43 | } elseif ($saveTimezone != null) { 44 | $value = $date->setTimezone(new DateTimeZone($saveTimezone))->format($saveFormat); 45 | } else { 46 | $value = $date->format($saveFormat); 47 | } 48 | echo Json::encode(['status' => 'success', 'output' => $value]); 49 | } else { 50 | echo Json::encode(['status' => 'error', 'output' => 'No display date found']); 51 | } 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /helpers/ArrayHelper.php: -------------------------------------------------------------------------------- 1 | 11 | * @since 2.0 12 | */ 13 | class ArrayHelper extends BaseArrayHelper 14 | { 15 | 16 | /** 17 | * Indexes an array according to a specified key. 18 | * The input array should be multidimensional or an array of objects. 19 | * 20 | * The key can be a key name of the sub-array, a property name of object, or an anonymous 21 | * function which returns the key value given an array element. 22 | * 23 | * If a key value is null, the corresponding array element will be discarded and not put in the result. 24 | * 25 | * For example, 26 | * 27 | * ~~~ 28 | * $array = [ 29 | * ['id' => '123', 'data' => 'abc'], 30 | * ['id' => '345', 'data' => 'def'], 31 | * ]; 32 | * $result = ArrayHelper::index($array, 'id'); 33 | * // the result is: 34 | * // [ 35 | * // '123' => ['id' => '123', 'data' => 'abc'], 36 | * // '345' => ['id' => '345', 'data' => 'def'], 37 | * // ] 38 | * 39 | * // using anonymous function 40 | * $result = ArrayHelper::index($array, function ($element) { 41 | * return $element['id']; 42 | * }); 43 | * ~~~ 44 | * 45 | * @param array $array the array that needs to be indexed 46 | * @param string|\Closure $key the column name or anonymous function whose result will be used to index the array 47 | * @return array the indexed array 48 | */ 49 | public static function indexRecursive($array, $key) 50 | { 51 | $result = []; 52 | foreach ($array as $element) { 53 | $value = static::getValue($element, $key); 54 | $result[$value][] = $element; 55 | } 56 | 57 | return $result; 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /helpers/CMS.php: -------------------------------------------------------------------------------- 1 | request->getIsPost()) { 16 | $post = Yii::$app->request->post(); 17 | 18 | // If one of those 3 keys is set there was an actual form submit 19 | if (isset($post['save']) || isset($post['save-add']) || isset($post['save-close'])) { 20 | return true; 21 | } 22 | } 23 | 24 | return false; 25 | } 26 | } -------------------------------------------------------------------------------- /helpers/Inflector.php: -------------------------------------------------------------------------------- 1 | 16 | * @author Alexander Makarov 17 | * @since 2.0 18 | */ 19 | class Inflector extends BaseInflector 20 | { 21 | /** 22 | * @var array fallback map for transliteration used by [[transliterate()]] when intl isn't available. 23 | */ 24 | public static $transliteration = [ 25 | 'À' => 'A', 'Á' => 'A', 'Â' => 'A', 'Ã' => 'A', 'Ä' => 'A', 'Å' => 'A', 'Æ' => 'AE', 'Ç' => 'C', 26 | 'È' => 'E', 'É' => 'E', 'Ê' => 'E', 'Ë' => 'E', 'Ì' => 'I', 'Í' => 'I', 'Î' => 'I', 'Ï' => 'I', 27 | 'Ð' => 'D', 'Ñ' => 'N', 'Ò' => 'O', 'Ó' => 'O', 'Ô' => 'O', 'Õ' => 'O', 'Ö' => 'O', 'Ő' => 'O', 28 | 'Ø' => 'O', 'Ù' => 'U', 'Ú' => 'U', 'Û' => 'U', 'Ü' => 'U', 'Ű' => 'U', 'Ý' => 'Y', 'Þ' => 'TH', 29 | 'ß' => 'ss', 30 | 'à' => 'a', 'á' => 'a', 'â' => 'a', 'ã' => 'a', 'ä' => 'a', 'å' => 'a', 'æ' => 'ae', 'ç' => 'c', 31 | 'è' => 'e', 'é' => 'e', 'ê' => 'e', 'ë' => 'e', 'ì' => 'i', 'í' => 'i', 'î' => 'i', 'ï' => 'i', 32 | 'ð' => 'd', 'ñ' => 'n', 'ò' => 'o', 'ó' => 'o', 'ô' => 'o', 'õ' => 'o', 'ö' => 'o', 'ő' => 'o', 33 | 'ø' => 'o', 'ù' => 'u', 'ú' => 'u', 'û' => 'u', 'ü' => 'u', 'ű' => 'u', 'ý' => 'y', 'þ' => 'th', 34 | 'ÿ' => 'y', 35 | ]; 36 | 37 | /** 38 | * Returns transliterated version of a string. 39 | * 40 | * If intl extension isn't available uses fallback that converts latin characters only 41 | * and removes the rest. You may customize characters map via $transliteration property 42 | * of the helper. 43 | * 44 | * @param string $string input string 45 | * @param string|\Transliterator $transliterator either a [[Transliterator]] or a string 46 | * from which a [[Transliterator]] can be built. 47 | * @return string 48 | */ 49 | public static function transliterate($string, $transliterator = null) 50 | { 51 | return str_replace(array_keys(static::$transliteration), static::$transliteration, $string); 52 | } 53 | } -------------------------------------------------------------------------------- /helpers/LanguageHelper.php: -------------------------------------------------------------------------------- 1 | params['rtl'])) { 19 | return false; 20 | } 21 | 22 | if (in_array($language, Yii::$app->params['rtl'])) { 23 | return true; 24 | }; 25 | 26 | return false; 27 | } 28 | 29 | /** 30 | * Returns the different parts of the provided or current language code. 31 | * "en-US" => ['en', 'us'] 32 | * 33 | * @return array 34 | */ 35 | public static function getLanguageCodeParts($language = null) 36 | { 37 | return array_map('strtolower', explode('-', ($language) ? $language : Yii::$app->language)); 38 | } 39 | 40 | /** 41 | * Returns only the language part of the provided or current language code. 42 | * "en-US" => 'en' 43 | * 44 | * @return string 45 | */ 46 | public static function getLanguage($language = null) 47 | { 48 | return self::getLanguageCodeParts($language)[0]; 49 | } 50 | 51 | /** 52 | * Returns only the country part of the language code. 53 | * "en-US" => 'us' 54 | * 55 | * @return string 56 | */ 57 | public static function getCountry($language = null) 58 | { 59 | $parts = self::getLanguageCodeParts($language); 60 | 61 | return (isset($parts[1])) ? $parts[1] : ''; 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /mail/contact.php: -------------------------------------------------------------------------------- 1 | 2 | 3 |

4 | 5 |

6 | 7 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 |
firstName ?>
name ?>
phone ?>
email ?>
body) ?>
-------------------------------------------------------------------------------- /mail/layouts/html.php: -------------------------------------------------------------------------------- 1 | 11 | beginPage() ?> 12 | 13 | 14 | 15 | 16 | 17 | head();*/ ?> 18 | 19 | 20 | 21 | 22 | 23 | 43 | 44 | 45 |
24 |
25 | 26 | 27 | 32 | 33 | 34 | 39 | 40 |
28 | 29 | baseUrl.'/img/logo-iw-dark.png', true), ['alt' => 'Infoweb Internet Solutions', 'width' => '45px']); ?> 30 | 31 |
35 | beginBody()*/ ?> 36 | 37 | endBody()*/ ?> 38 |
41 |
42 |
46 | 47 | 48 | 49 | 62 | 63 | 64 | 65 | 66 | 67 | endPage() ?> -------------------------------------------------------------------------------- /messages/nl/app.php: -------------------------------------------------------------------------------- 1 | '"{item}" en onderliggende zijn verwijderd', 5 | '"{item}" has been created' => '"{item}" is toegevoegd', 6 | '"{item}" has been deleted' => '"{item}" is verwijderd', 7 | '"{item}" has been updated' => '"{item}" is gewijzigd', 8 | '-- Choose a {item} --' => '-- Selecteer een {item} --', 9 | '-- Choose an {item} --' => '-- Selecteer een {item} --', 10 | '-- Choose the {items} --' => '-- Selecteer de {items} --', 11 | 'Account' => 'Account', 12 | 'Actions' => 'Acties', 13 | 'Active' => 'Actief', 14 | 'Add {modelClass}' => 'Voeg {modelClass} toe', 15 | 'Alt' => 'Alt-tekst', 16 | 'Are you sure you want to delete "{modelName}" and all related {modelClass}?' => '', 17 | 'Are you sure you want to delete this item(s)' => '', 18 | 'Are you sure you want to delete this item(s)?' => 'Ben je zeker dat je deze item(s) wilt verwijderen?', 19 | 'Are you sure you want to delete this item?' => 'Ben je zeker dat je dit item wilt verwijderen?', 20 | 'Are you sure you want to delete {n, plural, =1{this image} other{# images}}?' => 'Ben je zeker dat je deze {n, plural, =1{afbeelding} other{# afbeeldingen}} wilt verwijderen?', 21 | 'Assigments' => 'Toewijzingen', 22 | 'Attached to' => 'Gekoppeld aan', 23 | 'Brief description' => 'Korte beschrijving', 24 | 'Browse' => 'Bladeren', 25 | 'Cancel' => 'Annuleren', 26 | 'Category' => 'Categorie', 27 | 'Choose a category' => 'Kies een categorie', 28 | 'Choose a type' => 'Selecteer een type', 29 | 'Choose a {item}' => 'Kies een {item}', 30 | 'Choose the amount of {item}' => 'Kies het aantal {item}', 31 | 'Close' => 'Sluiten', 32 | 'Collapse all' => 'Verberg alles', 33 | 'Company' => 'Bedrijf', 34 | 'Content' => 'Inhoud', 35 | 'Copy to all languages' => '', 36 | 'Create' => 'Aanmaken', 37 | 'Create & close' => 'Aanmaken en sluiten', 38 | 'Create & new' => 'Aanmaken en toevoegen', 39 | 'Create {modelClass}' => '{modelClass} toevoegen', 40 | 'Created At' => 'Toegevoegd op', 41 | 'Dashboard' => 'Dashboard', 42 | 'Data' => 'Data', 43 | 'Date' => 'Datum', 44 | 'Delete' => 'Verwijderen', 45 | 'Delete {modelClass}' => 'Verwijder {modelClass}', 46 | 'Description' => 'Beschrijving', 47 | 'Drag & drop files here ...' => 'Sleep hier je bestanden naartoe ...', 48 | 'Drag and drop the images to change the sort order' => 'Sleep de afbeeldingen om de sorteervolgorde aan te passen', 49 | 'Edit {modelClass}' => 'Wijzig {modelClass}', 50 | 'Email' => 'E-mail', 51 | 'Entity' => 'Gekoppeld aan', 52 | 'Entity ID' => 'Koppeling', 53 | 'Example' => '', 54 | 'Expand all' => 'Toon alles', 55 | 'File' => 'Bestand', 56 | 'File Path' => 'Bestandslocatie', 57 | 'Firstname' => 'Voornaam', 58 | 'General' => 'Algemeen', 59 | 'ID' => 'ID', 60 | 'Image' => 'Afbeelding', 61 | 'Image is required' => 'Afbeelding is verplicht', 62 | 'Images' => 'Afbeeldingen', 63 | 'Images successfully uploaded' => 'Afbeeldingen met succes toegevoegd', 64 | 'Item ID' => 'Item', 65 | 'Key' => '', 66 | 'Label' => '', 67 | 'Language' => 'Taal', 68 | 'Logout' => 'Uitloggen', 69 | 'Main image' => 'Hoofdafbeelding', 70 | 'Media' => 'Media', 71 | 'Modules' => 'Modules', 72 | 'My profile' => 'Mijn profiel', 73 | 'Name' => 'Naam', 74 | 'No' => 'Nee', 75 | 'Not uploaded yet' => 'Nog niet ge-upload', 76 | 'PDF' => '', 77 | 'Pdf' => '', 78 | 'Permissions' => 'Permissies', 79 | 'Phone' => 'Telefoon', 80 | 'Picture' => 'Afbeelding', 81 | 'Position' => 'Positie', 82 | 'Remove' => 'Verwijderen', 83 | 'Remove file' => 'Verwijder bestand', 84 | 'Remove selected files' => 'Verwijder de geselecteerde bestanden', 85 | 'Rights' => 'Rechten', 86 | 'Role' => 'Rol', 87 | 'Roles' => 'Rollen', 88 | 'Routes' => 'Routes', 89 | 'Rules' => 'Regels', 90 | 'SKU' => 'Artikelnummer', 91 | 'Select a slider' => '', 92 | 'Select a {item}' => 'Selecteer een {item}', 93 | 'Select an {item}' => 'Selecteer een {item}', 94 | 'Set as main image' => 'Instellen als hoofdafbeelding', 95 | 'Show on homepage' => 'Tonen op homepagina', 96 | 'Show on homepage from' => 'Tonen op homepagina van', 97 | 'Show on homepage until' => 'Tonen op homepagina tot', 98 | 'Sort' => 'Sorteren', 99 | 'Sort {modelClass}' => 'Sorteren', 100 | 'Subtitle' => 'Subtitel', 101 | 'System' => 'Systeem', 102 | 'System name' => 'Systeemnaam', 103 | 'Template' => 'Template', 104 | 'The combination of Attribute ID and Language has already been taken.' => '', 105 | 'The combination of Entity and Entity ID has already been taken.' => 'Er bestaat al een alias voor deze combinatie', 106 | 'The item has been deleted' => 'Het item werd verwijderd', 107 | 'The sorting was successfully updated' => 'De sortering is gewijzigd', 108 | 'Title' => 'Titel', 109 | 'Toggle' => '', 110 | 'Toggle active' => 'Activeren / deactiveren', 111 | 'Toggle navigation' => 'Toon / verberg de navigatie', 112 | 'Translateable' => '', 113 | 'Translations' => 'Vertalingen', 114 | 'Type' => 'Type', 115 | 'Update' => 'Wijzigen', 116 | 'Update & close' => 'Wijzigen en sluiten', 117 | 'Update & new' => 'Wijzigen en toevoegen', 118 | 'Update {modelClass}' => '{modelClass} wijzigen', 119 | 'Update {modelClass}: ' => '{modelClass} wijzigen', 120 | 'Updated At' => 'Gewijzigd op', 121 | 'Updated at' => '', 122 | 'Upload' => 'Uploaden', 123 | 'Upload Error' => '', 124 | 'Upload file' => '', 125 | 'Upload selected files' => 'Upload de geselecteerde bestanden', 126 | 'Uploaded' => 'Ge-upload', 127 | 'Uploading ...' => 'Uploading ...', 128 | 'Url' => 'Url', 129 | 'Url alias' => 'Url alias', 130 | 'User' => 'Gebruiker', 131 | 'User defined' => 'Gebruiker', 132 | 'Users' => 'Gebruikers', 133 | 'View' => 'Bekijken', 134 | 'View attached file' => 'Bekijk het gekoppelde bestand', 135 | 'View {modelClass}' => 'Bekijk {modelClass}', 136 | 'Website' => 'Website', 137 | 'Welcome' => 'Welkom', 138 | 'Wijzigen' => '', 139 | 'Yes' => 'Ja', 140 | 'You do not have the right permissions to delete this item' => 'Je beschikt niet over voldoende rechten om dit item te verwijderen', 141 | 'field_id' => 'Expertise', 142 | '{item} has been deleted' => '{item} is verwijderd', 143 | '{item} has been updated' => '{item} is gewijzigd', 144 | '{item} successfully created' => '{item} succesvol toegevoegd', 145 | '{item} successfully updated' => '{item} succesvol gewijzigd', 146 | '{n, plural, =1{Image} other{# Images}} successfully deleted' => '{n, plural, =1{Afbeelding} other{# Afbeeldingen}} met succes verwijderd', 147 | '{n, plural, =1{Image} other{# images}} successfully uploaded' => '{n, plural, =1{Afbeelding} other{# afbeeldingen}} met success geupload', 148 | ]; 149 | -------------------------------------------------------------------------------- /messages/nl/infoweb/cms.php: -------------------------------------------------------------------------------- 1 | '"{item}" is toegevoegd', 5 | '"{item}" has been updated' => '"{item}" is gewijzigd', 6 | '-- Choose a {item} --' => '-- Selecteer een {item} --', 7 | '-- Choose an {item} --' => '-- Selecteer een {item} --', 8 | '-- Choose the {items} --' => '-- Selecteer de {items} --', 9 | 'Active' => 'Actief', 10 | 'Add {modelClass}' => 'Voeg {modelClass} toe', 11 | 'Alt' => 'Alt-tekst', 12 | 'Assigments' => 'Toewijzingen', 13 | 'Category' => 'Categorie', 14 | 'Choose a category' => 'Kies een categorie', 15 | 'Choose a type' => 'Selecteer een type', 16 | 'Choose a {item}' => 'Kies een {item}', 17 | 'Close' => 'Sluiten', 18 | 'Content' => 'Inhoud', 19 | 'Create' => 'Aanmaken', 20 | 'Create & close' => 'Aanmaken en sluiten', 21 | 'Create & new' => 'Aanmaken en toevoegen', 22 | 'Create {modelClass}' => '{modelClass} toevoegen', 23 | 'Created At' => 'Toegevoegd op', 24 | 'Dashboard' => 'Dashboard', 25 | 'Delete' => 'Verwijderen', 26 | 'Description' => 'Beschrijving', 27 | 'Duplicate' => 'Dupliceren', 28 | 'Duplicate content' => 'Content dupliceren', 29 | 'Duplicate empty fields' => 'Lege velden dupliceren', 30 | 'Edit {modelClass}' => 'Wijzig {modelClass}', 31 | 'Entity' => 'Gekoppeld aan', 32 | 'Entity ID' => 'Koppeling', 33 | 'Function' => 'Functie', 34 | 'General' => 'Algemeen', 35 | 'ID' => 'ID', 36 | 'Image' => 'Afbeelding', 37 | 'Language' => 'Taal', 38 | 'Languages' => 'Talen', 39 | 'Logout' => 'Uitloggen', 40 | 'Modules' => 'Modules', 41 | 'My profile' => 'Mijn profiel', 42 | 'Name' => 'Naam', 43 | 'No' => 'Nee', 44 | 'Options' => 'Opties', 45 | 'Overwrite populated fields' => 'Ingevulde velden overschrijven', 46 | 'Permissions' => 'Permissies', 47 | 'Picture' => 'Afbeelding', 48 | 'Position' => 'Positie', 49 | 'Rights' => 'Rechten', 50 | 'Roles' => 'Rollen', 51 | 'Routes' => 'Routes', 52 | 'Rules' => 'Regels', 53 | 'Show on homepage' => 'Weergeven op de homepagina', 54 | 'Show on homepage from' => 'Weergeven op de homepagina van', 55 | 'Show on homepage until' => 'Weergeven op de homepagina tot', 56 | 'Sort' => 'Sorteren', 57 | 'Sort {modelClass}' => 'Sorteren', 58 | 'System' => 'Systeem', 59 | 'Template' => 'Template', 60 | 'The combination of Entity and Entity ID has already been taken.' => 'Er bestaat al een alias voor deze combinatie', 61 | 'The item has been deleted' => 'Het item werd verwijderd', 62 | 'The sorting was successfully updated' => 'De sortering is gewijzigd', 63 | 'Title' => 'Titel', 64 | 'Toggle active' => 'Activeren / deactiveren', 65 | 'Toggle navigation' => 'Toon / verberg de navigatie', 66 | 'Translations' => 'Vertalingen', 67 | 'Type' => 'Type', 68 | 'Update' => 'Wijzigen', 69 | 'Update & close' => 'Wijzigen en sluiten', 70 | 'Update & new' => 'Wijzigen en toevoegen', 71 | 'Update {modelClass}' => '{modelClass} wijzigen', 72 | 'Update {modelClass}: ' => '{modelClass} wijzigen', 73 | 'Updated At' => 'Gewijzigd op', 74 | 'Url' => 'Url', 75 | 'User defined' => 'Gebruiker', 76 | 'Users' => 'Gebruikers', 77 | 'View' => 'Bekijken', 78 | 'View website' => 'Website bekijken', 79 | 'Welcome' => 'Welkom', 80 | 'Yes' => 'Ja', 81 | 'You do not have the right permissions to delete this item' => 'Je beschikt niet over voldoende rechten om dit item te verwijderen', 82 | 'field_id' => 'Expertise', 83 | '{item} has been deleted' => '{item} is verwijderd', 84 | '{item} has been updated' => '{item} is gewijzigd', 85 | '{item} successfully created' => '{item} succesvol toegevoegd', 86 | '{item} successfully updated' => '{item} succesvol gewijzigd', 87 | ]; 88 | -------------------------------------------------------------------------------- /migrations/m140930_123135_create_default_auth.php: -------------------------------------------------------------------------------- 1 | insert('{{%auth_item}}', [ 12 | 'name' => 'Superadmin', 13 | 'type' => 1, 14 | 'description' => 'Superadmin', 15 | 'created_at' => time(), 16 | 'updated_at' => time() 17 | ]); 18 | 19 | $this->insert('{{%auth_item}}', [ 20 | 'name' => '/*', 21 | 'type' => 2, 22 | 'description' => '', 23 | 'created_at' => time(), 24 | 'updated_at' => time() 25 | ]); 26 | 27 | // Assign the auth item to the main user 28 | $this->insert('{{%auth_assignment}}', [ 29 | 'item_name' => 'Superadmin', 30 | 'user_id' => 1, 31 | 'created_at' => time() 32 | ]); 33 | 34 | // Create the auth item relation 35 | $this->insert('{{%auth_item_child}}', [ 36 | 'parent' => 'Superadmin', 37 | 'child' => '/*' 38 | ]); 39 | } 40 | 41 | public function down() 42 | { 43 | echo "m140930_123135_create_default_permissions cannot be reverted.\n"; 44 | 45 | return false; 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /migrations/m141001_090124_add_default_permissions.php: -------------------------------------------------------------------------------- 1 | insert('{{%auth_item}}', [ 12 | 'name' => 'showContentModule', 13 | 'type' => 2, 14 | 'description' => 'Show content module icon in main-menu', 15 | 'created_at' => time(), 16 | 'updated_at' => time() 17 | ]); 18 | 19 | $this->insert('{{%auth_item}}', [ 20 | 'name' => 'showModulesModule', 21 | 'type' => 2, 22 | 'description' => 'Show modules module icon in main-menu', 23 | 'created_at' => time(), 24 | 'updated_at' => time() 25 | ]); 26 | 27 | $this->insert('{{%auth_item}}', [ 28 | 'name' => 'showRightsModule', 29 | 'type' => 2, 30 | 'description' => 'Show rights module icon in main-menu', 31 | 'created_at' => time(), 32 | 'updated_at' => time() 33 | ]); 34 | 35 | $this->insert('{{%auth_item}}', [ 36 | 'name' => 'showUsersModule', 37 | 'type' => 2, 38 | 'description' => 'Show users module icon in main-menu', 39 | 'created_at' => time(), 40 | 'updated_at' => time() 41 | ]); 42 | 43 | $this->insert('{{%auth_item}}', [ 44 | 'name' => 'showTranslationsModule', 45 | 'type' => 2, 46 | 'description' => 'Show translations module in main-menu', 47 | 'created_at' => time(), 48 | 'updated_at' => time() 49 | ]); 50 | 51 | $this->insert('{{%auth_item}}', [ 52 | 'name' => 'showMediaModule', 53 | 'type' => 2, 54 | 'description' => 'Show media module icon in main-menu', 55 | 'created_at' => time(), 56 | 'updated_at' => time() 57 | ]); 58 | 59 | $this->insert('{{%auth_item}}', [ 60 | 'name' => 'showAliasModule', 61 | 'type' => 2, 62 | 'description' => 'Show alias module icon in main-menu', 63 | 'created_at' => time(), 64 | 'updated_at' => time() 65 | ]); 66 | 67 | // Create the auth item relation 68 | $this->insert('{{%auth_item_child}}', [ 69 | 'parent' => 'Superadmin', 70 | 'child' => 'showContentModule' 71 | ]); 72 | 73 | $this->insert('{{%auth_item_child}}', [ 74 | 'parent' => 'Superadmin', 75 | 'child' => 'showModulesModule' 76 | ]); 77 | 78 | $this->insert('{{%auth_item_child}}', [ 79 | 'parent' => 'Superadmin', 80 | 'child' => 'showRightsModule' 81 | ]); 82 | 83 | $this->insert('{{%auth_item_child}}', [ 84 | 'parent' => 'Superadmin', 85 | 'child' => 'showUsersModule' 86 | ]); 87 | 88 | $this->insert('{{%auth_item_child}}', [ 89 | 'parent' => 'Superadmin', 90 | 'child' => 'showTranslationsModule' 91 | ]); 92 | 93 | $this->insert('{{%auth_item_child}}', [ 94 | 'parent' => 'Superadmin', 95 | 'child' => 'showMediaModule' 96 | ]); 97 | 98 | $this->insert('{{%auth_item_child}}', [ 99 | 'parent' => 'Superadmin', 100 | 'child' => 'showAliasModule' 101 | ]); 102 | } 103 | 104 | public function down() 105 | { 106 | // Delete the relations 107 | $this->delete('{{%auth_item_child}}', [ 108 | 'parent' => 'Superadmin', 109 | 'child' => 'showContentModule' 110 | ]); 111 | 112 | $this->delete('{{%auth_item_child}}', [ 113 | 'parent' => 'Superadmin', 114 | 'child' => 'showModulesModule' 115 | ]); 116 | 117 | $this->delete('{{%auth_item_child}}', [ 118 | 'parent' => 'Superadmin', 119 | 'child' => 'showRightsModule' 120 | ]); 121 | 122 | $this->delete('{{%auth_item_child}}', [ 123 | 'parent' => 'Superadmin', 124 | 'child' => 'showUsersModule' 125 | ]); 126 | 127 | $this->delete('{{%auth_item_child}}', [ 128 | 'parent' => 'Superadmin', 129 | 'child' => 'showTranslationsModule' 130 | ]); 131 | 132 | $this->delete('{{%auth_item_child}}', [ 133 | 'parent' => 'Superadmin', 134 | 'child' => 'showShopModule' 135 | ]); 136 | 137 | $this->delete('{{%auth_item_child}}', [ 138 | 'parent' => 'Superadmin', 139 | 'child' => 'showMediaModule' 140 | ]); 141 | 142 | $this->delete('{{%auth_item_child}}', [ 143 | 'parent' => 'Superadmin', 144 | 'child' => 'showCatalogueModule' 145 | ]); 146 | 147 | $this->delete('{{%auth_item_child}}', [ 148 | 'parent' => 'Superadmin', 149 | 'child' => 'showAliasModule' 150 | ]); 151 | 152 | // Delete the auth items 153 | $this->delete('{{%auth_item}}', [ 154 | 'name' => 'showContentModule', 155 | 'type' => 2, 156 | ]); 157 | 158 | $this->delete('{{%auth_item}}', [ 159 | 'name' => 'showModulesModule', 160 | 'type' => 2, 161 | ]); 162 | 163 | $this->delete('{{%auth_item}}', [ 164 | 'name' => 'showRightsModule', 165 | 'type' => 2, 166 | ]); 167 | 168 | $this->delete('{{%auth_item}}', [ 169 | 'name' => 'showUsersModule', 170 | 'type' => 2, 171 | ]); 172 | 173 | $this->delete('{{%auth_item}}', [ 174 | 'name' => 'showTranslationsModule', 175 | 'type' => 2, 176 | ]); 177 | 178 | $this->delete('{{%auth_item}}', [ 179 | 'name' => 'showMediaModule', 180 | 'type' => 2, 181 | ]); 182 | 183 | $this->delete('{{%auth_item}}', [ 184 | 'name' => 'showAliasModule', 185 | 'type' => 2, 186 | ]); 187 | } 188 | } 189 | -------------------------------------------------------------------------------- /migrations/m150107_074600_create_image_tables.php: -------------------------------------------------------------------------------- 1 | db->driverName === 'mysql') { 13 | $tableOptions = 'CHARACTER SET utf8 COLLATE utf8_unicode_ci ENGINE=InnoDB'; 14 | } 15 | 16 | // Drop image table 17 | if ($this->db->schema->getTableSchema('image', true)) { 18 | $this->dropTable('image'); 19 | } 20 | 21 | $this->createTable('{{%image}}', [ 22 | 'id' => Schema::TYPE_PK, 23 | 'name' => Schema::TYPE_STRING . '(255) NOT NULL', 24 | 'filePath' => Schema::TYPE_STRING . '(400) NOT NULL', 25 | 'itemId' => Schema::TYPE_STRING . '(255) NOT NULL', 26 | 'isMain' => 'TINYINT(3) UNSIGNED NOT NULL DEFAULT \'1\'', 27 | 'modelName' => Schema::TYPE_STRING . '(255) NOT NULL', 28 | 'urlAlias' => Schema::TYPE_STRING . '(255) NOT NULL', 29 | 'position' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 30 | 'active' => 'TINYINT(3) UNSIGNED NOT NULL DEFAULT \'1\'', 31 | 'created_at' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 32 | 'updated_at' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 33 | ], $tableOptions); 34 | 35 | $this->createIndex('itemId', '{{%image}}', 'itemId'); 36 | 37 | // Create 'image_lang' table 38 | $this->createTable('{{%image_lang}}', [ 39 | 'image_id' => Schema::TYPE_INTEGER . ' NOT NULL', 40 | 'language' => Schema::TYPE_STRING . '(10) NOT NULL', 41 | 'alt' => Schema::TYPE_STRING . '(255) NOT NULL', 42 | 'title' => Schema::TYPE_STRING . '(255) NOT NULL', 43 | 'subtitle' => Schema::TYPE_STRING . ' NOT NULL', 44 | 'description' => Schema::TYPE_TEXT . ' NOT NULL', 45 | 'url' => Schema::TYPE_STRING . '(255) NOT NULL', 46 | 'created_at' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 47 | 'updated_at' => Schema::TYPE_INTEGER . ' UNSIGNED NOT NULL', 48 | ], $tableOptions); 49 | 50 | $this->addPrimaryKey('image_lang_image_id_language', '{{%image_lang}}', ['image_id', 'language']); 51 | $this->createIndex('language', '{{%image_lang}}', 'language'); 52 | $this->addForeignKey('FK_IMAGE_LANG_IMAGE_ID', '{{%image_lang}}', 'image_id', '{{%image}}', 'id', 'CASCADE', 'NO ACTION'); 53 | } 54 | 55 | public function down() 56 | { 57 | $this->dropTable('image_lang'); 58 | $this->dropTable('image'); 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /migrations/m150526_084143_update_image_table.php: -------------------------------------------------------------------------------- 1 | alterColumn('image', 'isMain', 'TINYINT(3) UNSIGNED NOT NULL DEFAULT \'0\''); 10 | } 11 | 12 | public function safeDown() 13 | { 14 | $this->alterColumn('image', 'isMain', 'TINYINT(3) UNSIGNED NOT NULL DEFAULT \'1\''); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /migrations/m150602_110727_add_identifier_field.php: -------------------------------------------------------------------------------- 1 | addColumn('{{%image}}', 'identifier', Schema::TYPE_STRING.'(255) NOT NULL'); 11 | } 12 | 13 | public function down() 14 | { 15 | $this->dropColumn('{{%image}}', 'identifier'); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /migrations/m151012_130517_add_account_permissions.php: -------------------------------------------------------------------------------- 1 | insert('{{%auth_item}}', [ 11 | 'name' => 'showProfile', 12 | 'type' => 2, 13 | 'description' => 'Show profile icon in user dropdown', 14 | 'created_at' => time(), 15 | 'updated_at' => time() 16 | ]); 17 | 18 | $this->insert('{{%auth_item}}', [ 19 | 'name' => 'showAccount', 20 | 'type' => 2, 21 | 'description' => 'Show account icon in user dropdown', 22 | 'created_at' => time(), 23 | 'updated_at' => time() 24 | ]); 25 | 26 | $this->insert('{{%auth_item_child}}', [ 27 | 'parent' => 'Superadmin', 28 | 'child' => 'showProfile', 29 | ]); 30 | 31 | $this->insert('{{%auth_item_child}}', [ 32 | 'parent' => 'Superadmin', 33 | 'child' => 'showAccount', 34 | ]); 35 | } 36 | 37 | public function safeDown() 38 | { 39 | $this->delete('{{%auth_item_child}}', [ 40 | 'parent' => 'Superadmin', 41 | 'child' => 'showProfile' 42 | ]); 43 | 44 | $this->delete('{{%auth_item_child}}', [ 45 | 'parent' => 'Superadmin', 46 | 'child' => 'showAccount' 47 | ]); 48 | 49 | $this->delete('{{%auth_item}}', [ 50 | 'name' => 'showProfile', 51 | 'type' => 2, 52 | ]); 53 | 54 | $this->delete('{{%auth_item}}', [ 55 | 'name' => 'showAccount', 56 | 'type' => 2, 57 | ]); 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /migrations/m151118_084144_update_image_lang_table.php: -------------------------------------------------------------------------------- 1 | dropColumn('image_lang', 'url'); 11 | $this->addColumn('image_lang', 'link', Schema::TYPE_STRING . '(255) NOT NULL'); 12 | } 13 | 14 | public function safeDown() 15 | { 16 | $this->dropColumn('image_lang', 'link'); 17 | $this->addColumn('image_lang', 'url', Schema::TYPE_STRING . '(255) NOT NULL'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /migrations/m160614_090913_add_position_column.php: -------------------------------------------------------------------------------- 1 | addColumn('{{%image}}', 'text_position', Schema::TYPE_STRING.'(255) NOT NULL'); 12 | } 13 | 14 | public function safeDown() 15 | { 16 | $this->dropColumn('{{%image}}', 'text_position'); 17 | } 18 | } -------------------------------------------------------------------------------- /models/Image.php: -------------------------------------------------------------------------------- 1 | [ 24 | 'class' => TimestampBehavior::className(), 25 | 'attributes' => [ 26 | ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], 27 | ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at', 28 | ], 29 | 'value' => function () { 30 | return time(); 31 | }, 32 | ], 33 | 'translateable' => [ 34 | 'class' => TranslateableBehavior::className(), 35 | 'translationAttributes' => [ 36 | 'alt', 37 | 'title', 38 | 'subtitle', 39 | 'description', 40 | 'link' 41 | ] 42 | ], 43 | ]); 44 | } 45 | 46 | /** 47 | * @inheritdoc 48 | */ 49 | public function attributeLabels() 50 | { 51 | return [ 52 | 'name' => Yii::t('app', 'Image'), 53 | 'filePath' => Yii::t('app', 'File Path'), 54 | 'itemId' => Yii::t('app', 'Item ID'), 55 | 'isMain' => Yii::t('app', 'Main image'), 56 | 'modelName' => Yii::t('app', 'Attached to'), 57 | 'urlAlias' => Yii::t('app', 'Url alias'), 58 | 'active' => Yii::t('app', 'Active'), 59 | 'text_position' => Yii::t('app', 'Text position') 60 | ]; 61 | } 62 | 63 | public function textPositionLabels() { 64 | return [ 65 | self::TEXT_POSITION_LEFT => Yii::t('app', 'Left'), 66 | self::TEXT_POSITION_RIGHT => Yii::t('app', 'Right'), 67 | self::TEXT_POSITION_CENTER => Yii::t('app', 'Center'), 68 | ]; 69 | } 70 | 71 | public function getUrl($size = false, $crop = true, $fitImageInCanvas = false) 72 | { 73 | // Check if the image exists or return a placeholder 74 | if (!file_exists(Yii::getAlias('@uploadsBasePath/img/') . $this->filePath)) { 75 | $img = new PlaceHolder; 76 | return $img->getUrl($size); 77 | } 78 | 79 | $urlSize = ($size) ? '_'.$size : ''; 80 | $base = $this->getModule()->getCachePath(); 81 | $sub = $this->getSubDur(); 82 | $origin = $this->getPathToOrigin(); 83 | 84 | $filePath = $base.'/'.$sub.'/'.$this->urlAlias.$urlSize.'.'.pathinfo($origin, PATHINFO_EXTENSION); 85 | 86 | if(!file_exists($filePath)){ 87 | $this->createVersion($origin, $size, $crop, $fitImageInCanvas); 88 | 89 | if(!file_exists($filePath)){ 90 | throw new \Exception(Yii::t('app', 'The image does not exist')); 91 | } 92 | } 93 | 94 | $httpPath = \Yii::getAlias('@uploadsBaseUrl').'/img/cache/'.$sub.'/'.$this->urlAlias.$urlSize.'.'.pathinfo($origin, PATHINFO_EXTENSION); 95 | 96 | return $httpPath; 97 | } 98 | 99 | /** 100 | * Returns the path to the image 101 | * 102 | * @param string|boolean The size of the image 103 | * @param boolean Does cropping have to be applied 104 | * @return string 105 | * @uses infoweb\cms\models\Image::getUrl() 106 | */ 107 | public function getPath($size = false, $crop = true) 108 | { 109 | $url = $this->getUrl($size, $crop); 110 | 111 | // Replace the baseUrl with the basePath 112 | return str_replace(\Yii::getAlias('@uploadsBaseUrl'), \Yii::getAlias('@uploadsBasePath'), $url); 113 | } 114 | 115 | public function getBaseUrl() 116 | { 117 | $base = $this->getModule()->getStorePath(); 118 | $sub = $this->getSubDur(); 119 | $origin = $this->getPathToOrigin(); 120 | 121 | return $base.'/'.$this->filePath; 122 | } 123 | 124 | public function createVersion($imagePath, $sizeString = false, $crop = true, $fitImageInCanvas = false) 125 | { 126 | if(strlen($this->urlAlias)<1){ 127 | throw new \Exception('Image without urlAlias!'); 128 | } 129 | 130 | $cachePath = $this->getModule()->getCachePath(); 131 | $subDirPath = $this->getSubDur(); 132 | $fileExtension = pathinfo($this->filePath, PATHINFO_EXTENSION); 133 | 134 | if($sizeString){ 135 | $sizePart = '_'.$sizeString; 136 | } 137 | else{ 138 | $sizePart = ''; 139 | } 140 | 141 | $pathToSave = $cachePath.'/'.$subDirPath.'/'.$this->urlAlias.$sizePart.'.'.$fileExtension; 142 | 143 | BaseFileHelper::createDirectory(dirname($pathToSave), 0777, true); 144 | 145 | if($sizeString) { 146 | $size = $this->getModule()->parseSize($sizeString); 147 | } 148 | else{ 149 | $size = false; 150 | } 151 | 152 | if($this->getModule()->graphicsLibrary == 'Imagick'){ 153 | // Fixes interlaced images 154 | $interlaceFix = $this->interlaceFix($imagePath); 155 | 156 | if($interlaceFix) { 157 | $image = new \Imagick(); 158 | $image->readImageBlob($interlaceFix); 159 | } 160 | else { 161 | $image = new \Imagick($imagePath); 162 | } 163 | 164 | $image->setImageCompressionQuality(100); 165 | 166 | // Fixes image rotations 167 | $this->ImagickAutoRotateImage($image); 168 | 169 | // If the dimensions of the original image match the requested 170 | // dimensions the original image is just copied to the new path 171 | if ($image->getImageWidth() == $size['width'] && $image->getImageHeight() == $size['height']) { 172 | copy($imagePath, $pathToSave); 173 | return $image; 174 | } 175 | 176 | if($size){ 177 | if($size['height'] && $size['width']){ 178 | if(!$crop && $fitImageInCanvas) { 179 | $image = $this->fitImageInCanvas($image, $size['width'], $size['height']); 180 | } elseif ($crop) { 181 | $image->cropThumbnailImage($size['width'], $size['height']); 182 | } else { 183 | $image->resizeImage($size['width'], $size['height'], \Imagick::FILTER_HAMMING, 1, false); 184 | } 185 | } elseif($size['height']){ 186 | $image->thumbnailImage(0, $size['height']); 187 | } elseif($size['width']){ 188 | $image->thumbnailImage($size['width'], 0); 189 | } else{ 190 | throw new \Exception('Something wrong with this->module->parseSize($sizeString)'); 191 | } 192 | } 193 | 194 | $image->writeImage($pathToSave); 195 | } else { 196 | $image = new \abeautifulsite\SimpleImage($imagePath); 197 | 198 | // If the dimensions of the original image match the requested 199 | // dimensions the original image is just copied to the new path 200 | if ($image->get_width() == $size['width'] && $image->get_height() == $size['height']) { 201 | copy($imagePath, $pathToSave); 202 | return $image; 203 | } 204 | 205 | if($size){ 206 | if($size['height'] && $size['width']){ 207 | if($crop) { 208 | $image->thumbnail($size['width'], $size['height']); 209 | } 210 | else { 211 | $image->resize($size['width'], $size['height']); 212 | } 213 | }elseif($size['height']){ 214 | $image->fit_to_height($size['height']); 215 | }elseif($size['width']){ 216 | $image->fit_to_width($size['width']); 217 | }else{ 218 | throw new \Exception('Something wrong with this->module->parseSize($sizeString)'); 219 | } 220 | } 221 | 222 | //WaterMark 223 | if($this->getModule()->waterMark){ 224 | 225 | if(!file_exists(Yii::getAlias($this->getModule()->waterMark))){ 226 | throw new Exception('WaterMark not detected!'); 227 | } 228 | 229 | $wmMaxWidth = intval($image->get_width()*0.4); 230 | $wmMaxHeight = intval($image->get_height()*0.4); 231 | 232 | $waterMarkPath = Yii::getAlias($this->getModule()->waterMark); 233 | 234 | $waterMark = new \abeautifulsite\SimpleImage($waterMarkPath); 235 | 236 | 237 | 238 | if( 239 | $waterMark->get_height() > $wmMaxHeight 240 | or 241 | $waterMark->get_width() > $wmMaxWidth 242 | ){ 243 | 244 | $waterMarkPath = $this->getModule()->getCachePath().DIRECTORY_SEPARATOR. 245 | pathinfo($this->getModule()->waterMark)['filename']. 246 | $wmMaxWidth.'x'.$wmMaxHeight.'.'. 247 | pathinfo($this->getModule()->waterMark)['extension']; 248 | 249 | //throw new Exception($waterMarkPath); 250 | if(!file_exists($waterMarkPath)){ 251 | $waterMark->fit_to_width($wmMaxWidth); 252 | $waterMark->save($waterMarkPath, 100); 253 | if(!file_exists($waterMarkPath)){ 254 | throw new Exception('Cant save watermark to '.$waterMarkPath.'!!!'); 255 | } 256 | } 257 | 258 | } 259 | 260 | $image->overlay($waterMarkPath, 'bottom right', .5, -10, -10); 261 | 262 | } 263 | 264 | $image->save($pathToSave, 100); 265 | } 266 | 267 | return $image; 268 | } 269 | 270 | public function fitImageInCanvas(\Imagick $image, $width, $height) { 271 | $imageHeight = $image->getImageHeight(); 272 | $imageWidth = $image->getImageWidth(); 273 | 274 | if ($imageWidth > $width) { 275 | $image->scaleImage($width, $height, true); 276 | } 277 | if ($imageHeight > $height) { 278 | $image->scaleImage($width, $height, true); 279 | } 280 | 281 | $oldWidth = $image->getImageWidth(); 282 | $oldHeight = $image->getImageHeight(); 283 | 284 | #coords to center image inside fixed width/height canvas 285 | $x = ($width - $oldWidth) / 2; 286 | $y = ($height - $oldHeight) / 2; 287 | 288 | #create new image with the user image centered 289 | $newImage = new \Imagick(); 290 | $bgColor = ($image->getImageFormat() == 'png') ? 'none' : 'white'; 291 | $newImage->newImage($width, $height, new \ImagickPixel($bgColor)); 292 | $newImage->compositeImage($image, \Imagick::COMPOSITE_OVER, $x, $y); 293 | 294 | return $newImage; 295 | } 296 | 297 | 298 | 299 | /** 300 | * Fix interlaceFix 301 | * 302 | * @param type $file 303 | * @return boolean 304 | */ 305 | public function interlaceFix($file) { 306 | $ext = strtolower(pathinfo($file, PATHINFO_EXTENSION)); 307 | 308 | if ($ext == 'png') { 309 | $phpimage = imagecreatefrompng($file); 310 | 311 | imagealphablending($phpimage, false); 312 | imagesavealpha($phpimage, true); 313 | 314 | if ($phpimage) { 315 | imageinterlace($phpimage, 0); 316 | ob_start(); 317 | imagepng($phpimage); 318 | } 319 | else { 320 | unset($phpimage); 321 | } 322 | } 323 | elseif ($ext == 'jpeg') { 324 | $phpimage = imagecreatefromjpeg($file); 325 | 326 | if ($phpimage) { 327 | imageinterlace($phpimage, 0); 328 | ob_start(); 329 | imagejpeg($phpimage); 330 | } 331 | else { 332 | unset($phpimage); 333 | } 334 | } 335 | 336 | if (isset($phpimage)) { 337 | imagedestroy($phpimage); 338 | return ob_get_clean(); 339 | } 340 | 341 | return false; 342 | } 343 | 344 | /** 345 | * Automatic rotation fix 346 | * 347 | * @param \Imagick $image 348 | */ 349 | public function ImagickAutoRotateImage(\Imagick $image) { 350 | $orientation = $image->getImageOrientation(); 351 | switch ($orientation) { 352 | case \imagick::ORIENTATION_BOTTOMRIGHT : 353 | $image->rotateimage("#000", 180); 354 | // rotate 180 degrees 355 | break; 356 | 357 | case \imagick::ORIENTATION_RIGHTTOP : 358 | $image->rotateimage("#000", 90); 359 | // rotate 90 degrees CW 360 | break; 361 | 362 | case \imagick::ORIENTATION_LEFTBOTTOM : 363 | $image->rotateimage("#000", -90); 364 | // rotate 90 degrees CCW 365 | break; 366 | } 367 | 368 | $image->setImageOrientation(\imagick::ORIENTATION_TOPLEFT); 369 | } 370 | 371 | /** 372 | * @inheritdoc 373 | */ 374 | public function rules() 375 | { 376 | return [ 377 | // @todo Update rules 378 | //[['filePath', 'itemId', 'modelName', 'urlAlias'], 'required'], 379 | [['itemId', 'isMain', 'created_at', 'updated_at'], 'integer'], 380 | [['filePath', 'urlAlias'], 'string', 'max' => 400], 381 | [['modelName', 'identifier', 'text_position'], 'string', 'max' => 255], 382 | ['text_position', 'default', 'value' => self::TEXT_POSITION_LEFT] 383 | ]; 384 | } 385 | 386 | /** 387 | * @return \yii\db\ActiveQuery 388 | */ 389 | public function getTranslations() 390 | { 391 | return $this->hasMany(ImageLang::className(), ['image_id' => 'id']); 392 | } 393 | 394 | public function getPopupImage() 395 | { 396 | return ''; 397 | } 398 | 399 | public function clearCache(){ 400 | $subDir = $this->getSubDur(); 401 | $dirToRemove = $this->getModule()->getCachePath().DIRECTORY_SEPARATOR.$subDir; 402 | 403 | if(preg_match('/'.preg_quote($this->modelName, '/').'/', $dirToRemove)){ 404 | BaseFileHelper::removeDirectory($dirToRemove); 405 | } 406 | 407 | return true; 408 | } 409 | 410 | public function getFileInputWidgetPreview() 411 | { 412 | if ($this->name) { 413 | return [ 414 | Html::img($this->getUrl(), ['class' => 'file-preview-image', 'alt' => $this->alt, 'title' => $this->alt]) 415 | ]; 416 | } else { 417 | return []; 418 | } 419 | } 420 | 421 | public function getFileInputWidgetCaption() 422 | { 423 | return $this->name; 424 | } 425 | 426 | } -------------------------------------------------------------------------------- /models/ImageLang.php: -------------------------------------------------------------------------------- 1 | 10], 46 | [['link'], 'url', 'defaultScheme' => 'http'], 47 | [['description'], 'string'], 48 | [['alt', 'title', 'subtitle', 'link'], 'string', 'max' => 255], 49 | ]; 50 | } 51 | 52 | public function behaviors() 53 | { 54 | return ArrayHelper::merge(parent::behaviors(), [ 55 | 'timestamp' => [ 56 | 'class' => TimestampBehavior::className(), 57 | 'attributes' => [ 58 | ActiveRecord::EVENT_BEFORE_INSERT => ['created_at', 'updated_at'], 59 | ActiveRecord::EVENT_BEFORE_UPDATE => 'updated_at', 60 | ], 61 | 'value' => function() { return time(); }, 62 | ] 63 | ]); 64 | } 65 | 66 | /** 67 | * @inheritdoc 68 | */ 69 | public function attributeLabels() 70 | { 71 | return [ 72 | 'id' => Yii::t('app', 'ID'), 73 | 'image_id' => Yii::t('infoweb/sliders', 'Image ID'), 74 | 'alt' => Yii::t('app', 'Alt'), 75 | 'title' => Yii::t('app', 'Title'), 76 | 'subtitle' => Yii::t('app', 'Subtitle'), 77 | 'description' => Yii::t('app', 'Description'), 78 | 'created_at' => Yii::t('app', 'Created At'), 79 | 'updated_at' => Yii::t('app', 'Updated At'), 80 | ]; 81 | } 82 | 83 | /** 84 | * @return \yii\db\ActiveQuery 85 | */ 86 | public function getImage() 87 | { 88 | return $this->hasOne(Image::className(), ['id' => 'image_id']); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /models/ImageSearch.php: -------------------------------------------------------------------------------- 1 | where(['itemId' => $id]); 45 | 46 | if ($className) { 47 | $query->andWhere([ 48 | 'modelName' => StringHelper::basename($className), 49 | ]); 50 | } 51 | 52 | $dataProvider = new ActiveDataProvider([ 53 | 'query' => $query, 54 | 'sort' => ['defaultOrder' => ['position' => Yii::$app->getModule('cms')->imagesSorting ]] 55 | ]); 56 | 57 | if (!($this->load($params) && $this->validate())) { 58 | return $dataProvider; 59 | } 60 | 61 | $query->andFilterWhere([ 62 | 'id' => $this->id, 63 | 'name' => $this->name, 64 | ]); 65 | 66 | $query->andFilterWhere(['like', 'name', $this->name]); 67 | 68 | return $dataProvider; 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /models/ImageUploadForm.php: -------------------------------------------------------------------------------- 1 | false, /*'extensions' => 'jpg, png', 'mimeTypes' => 'image/jpeg, image/png',*/], 27 | ]; 28 | } 29 | 30 | /** 31 | * @inheritdoc 32 | */ 33 | public function attributeLabels() 34 | { 35 | return [ 36 | 'image' => Yii::t('app', 'Image') 37 | ]; 38 | } 39 | } -------------------------------------------------------------------------------- /models/MaskMoney.php: -------------------------------------------------------------------------------- 1 | 22 | * @since 1.0 23 | */ 24 | class MaskMoney extends \kartik\money\MaskMoney 25 | { 26 | /** 27 | * @inherit doc 28 | */ 29 | protected $_pluginName = 'maskMoney'; 30 | 31 | /** 32 | * @var array HTML attributes for the displayed input 33 | */ 34 | protected $_displayOptions = []; 35 | 36 | /** 37 | * @inherit doc 38 | */ 39 | public function init() 40 | { 41 | parent::init(); 42 | } 43 | 44 | /** 45 | * Registers the needed assets 46 | */ 47 | public function registerAssets() 48 | { 49 | $view = $this->getView(); 50 | MaskMoneyAsset::register($view); 51 | $id = 'jQuery("#' . $this->_displayOptions['id'] . '")'; 52 | $idSave = 'jQuery("#' . $this->options['id'] . '")'; 53 | $plugin = $this->_pluginName; 54 | $this->registerPlugin($plugin, $id); 55 | $js = <<< JS 56 | var val = parseFloat({$idSave}.val()) * 100; 57 | {$id}.{$plugin}('mask', val); 58 | {$id}.on('change', function () { 59 | var numDecimal = {$id}.{$plugin}('unmasked')[0]; 60 | {$idSave}.val(numDecimal); 61 | {$idSave}.trigger('change'); 62 | }); 63 | JS; 64 | $view->registerJs($js); 65 | } 66 | 67 | } 68 | -------------------------------------------------------------------------------- /models/PlaceHolder.php: -------------------------------------------------------------------------------- 1 | getModule()->placeHolderUrl; 31 | if(!$url){ 32 | throw new \Exception('PlaceHolder image must have url setting!!!'); 33 | } 34 | return $url; 35 | }*/ 36 | 37 | public function __construct() 38 | { 39 | $this->filePath =basename(Yii::getAlias($this->getModule()->placeHolderPath)) ; 40 | } 41 | 42 | public function getPathToOrigin() 43 | { 44 | 45 | $url = Yii::getAlias($this->getModule()->placeHolderPath); 46 | if (!$url) { 47 | throw new \Exception('PlaceHolder image must have path setting!!!'); 48 | } 49 | return $url; 50 | } 51 | 52 | protected function getSubDur(){ 53 | return 'placeHolder'; 54 | } 55 | public function setMain($isMain = true){ 56 | throw new yii\base\Exception('You must not set placeHolder as main image!!!'); 57 | } 58 | 59 | } 60 | 61 | -------------------------------------------------------------------------------- /views/layouts/_menu_sidebar.php: -------------------------------------------------------------------------------- 1 | {icon}{label}'; 8 | 9 | ?> 10 | -------------------------------------------------------------------------------- /views/layouts/_menu_top_right.php: -------------------------------------------------------------------------------- 1 | 6 | 7 | -------------------------------------------------------------------------------- /views/layouts/main.php: -------------------------------------------------------------------------------- 1 | params['cmsAssets'] = $cmsAssets; 16 | $this->registerJs("CMS.setCkeditorEntitylinkConfiguration(".json_encode(Yii::$app->getModule('menu')->getCkeditorEntitylinkConfiguration()).");", \yii\web\View::POS_READY, 'ckeditorEntitylinkConfiguration'); 17 | 18 | ?> 19 | beginPage() ?> 20 | 21 | 22 | 23 | 24 | 25 | 26 | <?= Html::encode($this->title) ?> 27 | 28 | head() ?> 29 | 30 | 31 | beginBody() ?> 32 | 33 | Html::img($cmsAssets->baseUrl.'/img/logo-infoweb.png', ['class' => 'brand-logo', 'alt' => 'brand-logo']), 37 | //'brandUrl' => Yii::$app->homeUrl, 38 | 'options' => [ 39 | 'class' => 'navbar-inverse navbar-fixed-top', 40 | ], 41 | 'containerOptions' => [ 42 | 'class' => 'in', 43 | ], 44 | 'renderInnerContainer' => false, 45 | ]); 46 | ?> 47 | 48 | 49 | user->isGuest) : ?> 50 | 60 | 61 | 62 | 63 | render('_menu_top_right') ?> 64 | 65 | 66 | 78 |
79 | */ ?> 80 | 81 | render('_menu_sidebar') ?> 82 | 83 | 84 | 85 | 86 | 87 | user->isGuest) : ?> 88 | 89 | 90 | isset($this->params['breadcrumbs']) ? $this->params['breadcrumbs'] : [], 92 | ]) ?> 93 | 94 | 95 | 96 | 97 |
params['breadcrumbs'])) ? 'class="breadcrumb-padding"' :''; ?>> 98 | 99 | 100 |
101 | © Infoweb 102 | */ ?> 103 |
104 |
105 | 106 | 107 | 'duplicateable-modal', 109 | 'header' => '

'.Yii::t('infoweb/cms', 'Duplicate content').'

', 110 | 'footer' => '' 111 | ]); ?> 112 |
113 |
114 |

115 |
    116 | params['languages'] as $k => $v) : ?> 117 |
  • 118 |
    119 | 122 |
    123 |
  • 124 | 125 |
126 |
127 |
128 |

129 |
    130 |
  • 131 |
    132 | 135 |
    136 |
  • 137 |
  • 138 |
    139 | 142 |
    143 |
  • 144 |
145 |
146 |
147 | 148 | 149 | endBody() ?> 150 | 151 | 152 | endPage() ?> -------------------------------------------------------------------------------- /views/layouts/media.php: -------------------------------------------------------------------------------- 1 | 6 | beginPage() ?> 7 | 8 | 9 | 10 | 11 | 12 | 13 | <?= Html::encode($this->title) ?> 14 | head() ?> 15 | 16 | 17 | beginBody() ?> 18 | 19 | 20 | 21 | endBody() ?> 22 | 23 | 24 | endPage() ?> -------------------------------------------------------------------------------- /views/media/index.php: -------------------------------------------------------------------------------- 1 | 6 | 15 | 16 | -------------------------------------------------------------------------------- /views/media/media.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /views/ui/formButtons.php: -------------------------------------------------------------------------------- 1 | 4 | isNewRecord ? Yii::t('app', 'Create') : Yii::t('app', 'Update'), [ 5 | 'class' => $model->isNewRecord ? 'btn btn-success' : 'btn btn-primary', 6 | 'name' => 'save' 7 | ]) ?>  8 | isNewRecord ? Yii::t('app', 'Create & close') : Yii::t('app', 'Update & close'), [ 9 | 'class' => 'btn btn-default', 10 | 'name' => 'save-close' 11 | ]) ?>  12 | isNewRecord ? 'Create & new' : 'Update & new'), [ 13 | 'class' => 'btn btn-default', 14 | 'name' => 'save-add' 15 | ]) ?>  16 | 'btn btn-danger', 18 | 'name' => 'close' 19 | ]) ?> --------------------------------------------------------------------------------