Revert "Rebase October CMS on Laravel 6 (#4893)"

This reverts commit ee0065d353.
This commit is contained in:
Luke Towers 2020-08-09 03:48:03 -06:00
parent ee0065d353
commit 60afd41cc2
118 changed files with 2105 additions and 1381 deletions

View File

@ -6,18 +6,25 @@ on:
jobs:
codeQuality:
runs-on: ubuntu-latest
name: PHPCS
name: PHP
steps:
- name: Checkout changes
uses: actions/checkout@v1
- name: Install PHP and PHP Code Sniffer
uses: shivammathur/setup-php@v1
- name: Install PHP
uses: shivammathur/setup-php@master
with:
php-version: '7.3'
tools: phpcs
- name: Setup problem matcher for PHPCS
run: echo "::add-matcher::${{ github.workspace }}/.github/workflows/matchers/phpcs-matcher.json"
php-version: 7.2
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-suggest
- name: Reset October modules and library
run: |
git reset --hard HEAD
rm -rf ./vendor/october/rain
wget https://github.com/octobercms/library/archive/develop.zip -O ./vendor/october/develop.zip
unzip ./vendor/october/develop.zip -d ./vendor/october
mv ./vendor/october/library-develop ./vendor/october/rain
composer dump-autoload
- name: Run code quality checks
run: |
git config remote.origin.fetch "+refs/heads/*:refs/remotes/origin/*" && git fetch
phpcs --colors -nq --report="checkstyle" --extensions="php" $(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }} HEAD)
./vendor/bin/phpcs --colors -nq --report="full" --extensions="php" $(git diff --name-only --diff-filter=ACMR origin/${{ github.base_ref }} HEAD)

View File

@ -9,16 +9,23 @@ on:
jobs:
codeQuality:
runs-on: ubuntu-latest
name: PHPCS
name: PHP
steps:
- name: Checkout changes
uses: actions/checkout@v1
- name: Install PHP and PHP Code Sniffer
uses: shivammathur/setup-php@v1
- name: Install PHP
uses: shivammathur/setup-php@master
with:
php-version: '7.3'
tools: phpcs
- name: Setup problem matcher for PHPCS
run: echo "::add-matcher::${{ github.workspace }}/.github/workflows/matchers/phpcs-matcher.json"
php-version: 7.2
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-suggest
- name: Reset October modules and library
run: |
git reset --hard HEAD
rm -rf ./vendor/october/rain
wget https://github.com/octobercms/library/archive/develop.zip -O ./vendor/october/develop.zip
unzip ./vendor/october/develop.zip -d ./vendor/october
mv ./vendor/october/library-develop ./vendor/october/rain
composer dump-autoload
- name: Run code quality checks
run: phpcs --colors -nq --report="checkstyle" --extensions="php" $(git show --name-only --pretty="" --diff-filter=ACMR ${{ github.sha }})
run: ./vendor/bin/phpcs --colors -nq --report="full" --extensions="php" $(git show --name-only --pretty="" --diff-filter=ACMR ${{ github.sha }})

24
.github/workflows/frontend-tests.yaml vendored Normal file
View File

@ -0,0 +1,24 @@
name: Tests
on:
push:
branches:
- master
- develop
pull_request:
jobs:
frontendTests:
runs-on: ubuntu-latest
name: JavaScript
steps:
- name: Checkout changes
uses: actions/checkout@v1
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 8
- name: Install Node dependencies
run: npm install
- name: Run tests
run: npm run test

View File

@ -1,23 +0,0 @@
{
"problemMatcher": [
{
"owner": "phpcs",
"severity": "error",
"pattern": [
{
"regexp": "^<file name=\"(.*)\">$",
"file": 1
},
{
"regexp": "<error line=\"(\\d*)\" column=\"(\\d*)\" severity=\"(error|warning)\" message=\"(.*)\" source=\"(.*)(\"\\/>+)$",
"line": 1,
"column": 2,
"severity": 3,
"message": 4,
"code": 5,
"loop": true
}
]
}
]
}

View File

@ -8,56 +8,37 @@ on:
pull_request:
jobs:
frontendTests:
runs-on: ubuntu-latest
name: JavaScript
steps:
- name: Checkout changes
uses: actions/checkout@v1
- name: Install Node
uses: actions/setup-node@v1
with:
node-version: 8
- name: Install Node dependencies
run: npm install
- name: Run tests
run: npm run test
phpUnitTests:
runs-on: ubuntu-latest
strategy:
max-parallel: 6
matrix:
phpVersions: ['7.2', '7.3', '7.4']
phpVersions: ['7.1', '7.2', '7.3', '7.4']
fail-fast: false
name: Unit Tests / PHP ${{ matrix.phpVersions }}
name: PHP ${{ matrix.phpVersions }}
steps:
- name: Checkout changes
uses: actions/checkout@v1
- name: Install PHP
uses: shivammathur/setup-php@v1
uses: shivammathur/setup-php@master
with:
php-version: ${{ matrix.phpVersions }}
extensions: mbstring, intl, gd, xml, sqlite
- name: Setup problem matchers for PHPUnit
run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json"
- name: Set Composer cache
id: composer-cache
run: echo "::set-output name=dir::$(composer config cache-files-dir)"
- name: Cache Composer dependencies
uses: actions/cache@v1
with:
path: ${{ steps.composer-cache.outputs.dir }}
key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }}
restore-keys: ${{ runner.os }}-composer-
extension-csv: mbstring, intl, gd, xml, sqlite
- name: Install Composer dependencies
run: composer install --no-interaction --no-progress --no-suggest --no-scripts
- name: Run post-update Composer scripts
run: php artisan package:discover
- name: Reset October modules
- name: Reset October modules and library
run: |
git reset --hard HEAD
composer dumpautoload
rm -rf ./vendor/october/rain
wget https://github.com/octobercms/library/archive/develop.zip -O ./vendor/october/develop.zip
unzip ./vendor/october/develop.zip -d ./vendor/october
mv ./vendor/october/library-develop ./vendor/october/rain
composer dump-autoload
- name: Run post-update Composer scripts
run: |
php artisan october:util set build
php artisan package:discover
- name: Run Linting and Tests
run: |
./vendor/bin/parallel-lint --exclude vendor --exclude storage --exclude tests/fixtures/plugins/testvendor/goto/Plugin.php .
./vendor/bin/phpunit --prepend ./vendor/october/rain/src/Support/helpers.php
./vendor/bin/phpunit

28
.gitignore vendored
View File

@ -1,32 +1,24 @@
# Composer ignores
/bootstrap/compiled.php
/vendor
composer.phar
composer.lock
# Framework ignores
.DS_Store
.idea
.env
.env.*.php
.env.php
selenium.php
/bootstrap/compiled.php
.phpunit.result.cache
# Hosting ignores
php_errors.log
nginx-error.log
nginx-access.log
nginx-ssl.access.log
nginx-ssl.error.log
php-errors.log
sftp-config.json
.ftpconfig
# Editor ignores
nbproject
.idea
.vscode
_ide_helper.php
# Other ignores
.DS_Store
selenium.php
composer.lock
package-lock.json
/node_modules
_ide_helper.php
# for netbeans
nbproject

View File

@ -28,7 +28,7 @@ $app = require_once __DIR__.'/bootstrap/app.php';
|
*/
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
$kernel = $app->make('Illuminate\Contracts\Console\Kernel');
$status = $kernel->handle(
$input = new Symfony\Component\Console\Input\ArgvInput,
@ -48,4 +48,4 @@ $status = $kernel->handle(
$kernel->terminate($input, $status);
exit($status);
exit($status);

View File

@ -35,3 +35,20 @@ require $helperPath;
*/
require __DIR__.'/../vendor/autoload.php';
/*
|--------------------------------------------------------------------------
| Include The Compiled Class File
|--------------------------------------------------------------------------
|
| To dramatically increase your application's performance, you may use a
| compiled class file which contains all of the classes commonly used
| by a request. The Artisan "optimize" is used to create this file.
|
*/
$compiledPath = __DIR__.'/../storage/framework/compiled.php';
if (file_exists($compiledPath)) {
require $compiledPath;
}

View File

@ -1,6 +1,6 @@
{
"name": "october/october",
"description": "October CMS",
"description": "OctoberCMS",
"homepage": "https://octobercms.com",
"type": "project",
"keywords": ["october", "cms", "octobercms", "laravel"],
@ -24,34 +24,37 @@
}
],
"support": {
"paid": "https://octobercms.com/premium-support",
"issues": "https://github.com/octobercms/october/issues",
"forum": "https://octobercms.com/forum/",
"docs": "https://octobercms.com/docs/",
"irc": "irc://irc.freenode.net/october",
"source": "https://github.com/octobercms/october"
},
"require": {
"php": ">=7.2",
"october/rain": "dev-develop as 1.0",
"october/system": "dev-develop",
"october/backend": "dev-develop",
"october/cms": "dev-develop",
"laravel/framework": "~6.0",
"php": ">=7.0.8",
"ext-mbstring": "*",
"ext-openssl": "*",
"october/rain": "~1.0",
"october/system": "~1.0",
"october/backend": "~1.0",
"october/cms": "~1.0",
"laravel/framework": "~5.5.40",
"wikimedia/composer-merge-plugin": "1.4.1"
},
"require-dev": {
"phpunit/phpunit": "^8.0|^9.0",
"fzaninotto/faker": "~1.9",
"fzaninotto/faker": "~1.7",
"phpunit/phpunit": "~6.5",
"phpunit/phpunit-selenium": "~1.2",
"meyfa/phpunit-assert-gd": "1.1.0",
"squizlabs/php_codesniffer": "3.*",
"php-parallel-lint/php-parallel-lint": "^1.0",
"meyfa/phpunit-assert-gd": "^2.0.0",
"dms/phpunit-arraysubset-asserts": "^0.1.0"
"php-parallel-lint/php-parallel-lint": "^1.0"
},
"autoload-dev": {
"classmap": [
"tests/concerns/InteractsWithAuthentication.php",
"tests/fixtures/backend/models/UserFixture.php",
"tests/TestCase.php",
"tests/UiTestCase.php",
"tests/PluginTestCase.php"
]
},
@ -63,21 +66,12 @@
"post-update-cmd": [
"php artisan october:util set build",
"php artisan package:discover"
],
"test": [
"phpunit --stop-on-failure"
],
"lint": [
"parallel-lint --exclude vendor --exclude storage --exclude tests/fixtures/plugins/testvendor/goto/Plugin.php ."
],
"sniff": [
"phpcs --colors -nq --report=\"full\" --extensions=\"php\""
]
},
"config": {
"preferred-install": "dist",
"platform": {
"php": "7.2"
"php": "7.0.8"
}
},
"minimum-stability": "dev",

View File

@ -111,6 +111,21 @@ return [
'cipher' => 'AES-256-CBC',
/*
|--------------------------------------------------------------------------
| Logging Configuration
|--------------------------------------------------------------------------
|
| Here you may configure the log settings for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Settings: "single", "daily", "syslog", "errorlog"
|
*/
'log' => 'single',
/*
|--------------------------------------------------------------------------
| Autoloaded Service Providers
@ -129,26 +144,6 @@ return [
'System\ServiceProvider',
]),
/*
|--------------------------------------------------------------------------
| Load automatically discovered packages
|--------------------------------------------------------------------------
|
| By default, October CMS disables the loading of discovered packages
| through Laravel's package discovery service, in order to allow packages
| used by plugins to be disabled if the plugin itself is disabled.
|
| Set this to `true` to enable automatic loading of these packages. This
| will result in packages being loaded, even if the plugin using them is
| disabled. This is NOT RECOMMENDED.
|
| Please note that packages defined in `app.providers` will still be loaded
| even if discovery is disabled.
|
*/
'loadDiscoveredPackages' => false,
/*
|--------------------------------------------------------------------------
| Class Aliases

View File

@ -116,7 +116,6 @@ return [
'redis' => [
'client' => 'predis',
'cluster' => false,
'default' => [

View File

@ -20,27 +20,5 @@ return [
*/
'decompileBackendAssets' => false,
/*
|--------------------------------------------------------------------------
| Allow deep-level symlinks
|--------------------------------------------------------------------------
|
| October CMS, by default, will allow symlinks within the first level of
| subdirectories. When this feature is enabled, the system will allow
| symlinks to be used at any directory level. This can be useful for
| symlinking individual plugins or themes.
|
| Please note that this has a negative effect on performance. This feature
| abides by "cms.restrictBaseDir" - if enabled, symlinks cannot point to
| resources outside of the root folder.
|
| true - allow symlinks at any level
|
| false - only allow symlinks at the first level of subdirectories (default)
|
*/
'allowDeepSymlinks' => false,
];

View File

@ -11,7 +11,7 @@ return [
| by the framework. A "local" driver, as well as a variety of cloud
| based drivers are available for your choosing. Just store away!
|
| Supported: "local", "ftp", "sftp", "s3", "rackspace"
| Supported: "local", "s3", "rackspace"
|
*/

View File

@ -1,52 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Hash Driver
|--------------------------------------------------------------------------
|
| This option controls the default hash driver that will be used to hash
| passwords for your application. By default, the bcrypt algorithm is
| used; however, you remain free to modify this option if you wish.
|
| Supported: "bcrypt", "argon", "argon2id"
|
*/
'driver' => 'bcrypt',
/*
|--------------------------------------------------------------------------
| Bcrypt Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Bcrypt algorithm. This will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'bcrypt' => [
'rounds' => env('BCRYPT_ROUNDS', 10),
],
/*
|--------------------------------------------------------------------------
| Argon Options
|--------------------------------------------------------------------------
|
| Here you may specify the configuration options that should be used when
| passwords are hashed using the Argon algorithm. These will allow you
| to control the amount of time it takes to hash the given password.
|
*/
'argon' => [
'memory' => 1024,
'threads' => 2,
'time' => 2,
],
];

View File

@ -1,91 +0,0 @@
<?php
return [
/*
|--------------------------------------------------------------------------
| Default Log Channel
|--------------------------------------------------------------------------
|
| This option defines the default log channel that gets used when writing
| messages to the logs. The name specified in this option should match
| one of the channels defined in the "channels" configuration array.
|
*/
'default' => env('LOG_CHANNEL', 'single'),
/*
|--------------------------------------------------------------------------
| Log Channels
|--------------------------------------------------------------------------
|
| Here you may configure the log channels for your application. Out of
| the box, Laravel uses the Monolog PHP logging library. This gives
| you a variety of powerful log handlers / formatters to utilize.
|
| Available Drivers: "single", "daily", "slack", "syslog",
| "errorlog", "monolog",
| "custom", "stack"
|
*/
'channels' => [
'stack' => [
'driver' => 'stack',
'channels' => ['daily'],
'ignore_exceptions' => false,
],
'single' => [
'driver' => 'single',
'path' => storage_path('logs/system.log'),
'level' => 'debug',
],
'daily' => [
'driver' => 'daily',
'path' => storage_path('logs/system.log'),
'level' => 'debug',
'days' => 14,
],
'slack' => [
'driver' => 'slack',
'url' => env('LOG_SLACK_WEBHOOK_URL'),
'username' => 'October CMS Log',
'emoji' => ':boom:',
'level' => 'critical',
],
'papertrail' => [
'driver' => 'monolog',
'level' => 'debug',
'handler' => \Monolog\Handler\SyslogUdpHandler::class,
'handler_with' => [
'host' => env('PAPERTRAIL_URL'),
'port' => env('PAPERTRAIL_PORT'),
],
],
'stderr' => [
'driver' => 'monolog',
'handler' => \Monolog\Handler\StreamHandler::class,
'formatter' => env('LOG_STDERR_FORMATTER'),
'with' => [
'stream' => 'php://stderr',
],
],
'syslog' => [
'driver' => 'syslog',
'level' => 'debug',
],
'errorlog' => [
'driver' => 'errorlog',
'level' => 'debug',
],
],
];

View File

@ -12,7 +12,7 @@ return [
| your application here. By default, Laravel is setup for SMTP mail.
|
| Supported: "smtp", "sendmail", "mailgun", "mandrill", "ses",
| "postmark", "sparkpost", "log", "array"
| "sparkpost", "log", "array"
|
*/

View File

@ -24,10 +24,6 @@ return [
'secret' => '',
],
'postmark' => [
'token' => '',
],
'ses' => [
'key' => '',
'secret' => '',

View File

@ -37,7 +37,7 @@ $app = require_once __DIR__.'/bootstrap/app.php';
|
*/
$kernel = $app->make(Illuminate\Contracts\Http\Kernel::class);
$kernel = $app->make('Illuminate\Contracts\Http\Kernel');
$response = $kernel->handle(
$request = Illuminate\Http\Request::capture()

View File

@ -80,7 +80,6 @@ class ServiceProvider extends ModuleServiceProvider
$combiner->registerBundle('~/modules/backend/formwidgets/colorpicker/assets/less/colorpicker.less');
$combiner->registerBundle('~/modules/backend/formwidgets/permissioneditor/assets/less/permissioneditor.less');
$combiner->registerBundle('~/modules/backend/formwidgets/markdowneditor/assets/less/markdowneditor.less');
$combiner->registerBundle('~/modules/backend/formwidgets/sensitive/assets/less/sensitive.less');
/*
* Rich Editor is protected by DRM
@ -200,7 +199,6 @@ class ServiceProvider extends ModuleServiceProvider
$manager->registerFormWidget('Backend\FormWidgets\TagList', 'taglist');
$manager->registerFormWidget('Backend\FormWidgets\MediaFinder', 'mediafinder');
$manager->registerFormWidget('Backend\FormWidgets\NestedForm', 'nestedform');
$manager->registerFormWidget('Backend\FormWidgets\Sensitive', 'sensitive');
});
}

View File

@ -11,7 +11,7 @@ use Backend\Behaviors\ImportExportController\TranscodeFilter;
use Illuminate\Database\Eloquent\MassAssignmentException;
use League\Csv\Reader as CsvReader;
use League\Csv\Writer as CsvWriter;
use League\Csv\EscapeFormula as CsvEscapeFormula;
use October\Rain\Parse\League\EscapeFormula as CsvEscapeFormula;
use ApplicationException;
use SplTempFileObject;
use Exception;
@ -624,7 +624,9 @@ class ImportExportController extends ControllerBehavior
$csv->setDelimiter($options['delimiter']);
$csv->setEnclosure($options['enclosure']);
$csv->setEscape($options['escape']);
$csv->addFormatter(new CsvEscapeFormula());
// Temporary until upgrading to league/csv >= 9.1.0 (will be $csv->addFormatter($formatter))
$formatter = new CsvEscapeFormula();
/*
* Add headers
@ -660,6 +662,9 @@ class ImportExportController extends ControllerBehavior
$record[] = $value;
}
// Temporary until upgrading to league/csv >= 9.1.0
$record = $formatter($record);
$csv->insertOne($record);
}

View File

@ -124,7 +124,7 @@ class FormField
/**
* @var string Specifies a comment to accompany the field
*/
public $comment = '';
public $comment;
/**
* @var string Specifies the comment position.
@ -139,7 +139,7 @@ class FormField
/**
* @var string Specifies a message to display when there is no value supplied (placeholder).
*/
public $placeholder = '';
public $placeholder;
/**
* @var array Contains a list of attributes specified in the field configuration.

View File

@ -8,13 +8,11 @@
"authors": [
{
"name": "Alexey Bobkov",
"email": "aleksey.bobkov@gmail.com",
"role": "Co-founder"
"email": "aleksey.bobkov@gmail.com"
},
{
"name": "Samuel Georges",
"email": "daftspunky@gmail.com",
"role": "Co-founder"
"email": "daftspunky@gmail.com"
},
{
"name": "Luke Towers",
@ -24,10 +22,9 @@
}
],
"require": {
"php": ">=7.2",
"php": ">=7.0",
"composer/installers": "~1.0",
"october/rain": "~1.0",
"laravel/framework": "~6.0"
"october/rain": "~1.0"
},
"autoload": {
"psr-4": {

View File

@ -12,8 +12,8 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
Eloquent::unguarded(function () {
$this->call('Backend\Database\Seeds\SeedSetupAdmin');
});
Eloquent::unguard();
$this->call('Backend\Database\Seeds\SeedSetupAdmin');
}
}

View File

@ -1,117 +0,0 @@
<?php namespace Backend\FormWidgets;
use Backend\Classes\FormWidgetBase;
/**
* Sensitive widget.
*
* Renders a password field that can be optionally made visible
*
* @package october\backend
*/
class Sensitive extends FormWidgetBase
{
/**
* @var bool If true, the sensitive field cannot be edited, but can be toggled.
*/
public $readOnly = false;
/**
* @var bool If true, the sensitive field is disabled.
*/
public $disabled = false;
/**
* @var bool If true, a button will be available to copy the value.
*/
public $allowCopy = false;
/**
* @var string The string that will be used as a placeholder for an unrevealed sensitive value.
*/
public $hiddenPlaceholder = '__hidden__';
/**
* @var bool If true, the sensitive input will be hidden if the user changes to another tab in their browser.
*/
public $hideOnTabChange = true;
/**
* @inheritDoc
*/
protected $defaultAlias = 'sensitive';
/**
* @inheritDoc
*/
public function init()
{
$this->fillFromConfig([
'readOnly',
'disabled',
'allowCopy',
'hiddenPlaceholder',
'hideOnTabChange',
]);
if ($this->formField->disabled || $this->formField->readOnly) {
$this->previewMode = true;
}
}
/**
* @inheritDoc
*/
public function render()
{
$this->prepareVars();
return $this->makePartial('sensitive');
}
/**
* Prepares the view data for the widget partial.
*/
public function prepareVars()
{
$this->vars['readOnly'] = $this->readOnly;
$this->vars['disabled'] = $this->disabled;
$this->vars['hasValue'] = !empty($this->getLoadValue());
$this->vars['allowCopy'] = $this->allowCopy;
$this->vars['hiddenPlaceholder'] = $this->hiddenPlaceholder;
$this->vars['hideOnTabChange'] = $this->hideOnTabChange;
}
/**
* Reveals the value of a hidden, unmodified sensitive field.
*
* @return array
*/
public function onShowValue()
{
return [
'value' => $this->getLoadValue()
];
}
/**
* @inheritDoc
*/
public function getSaveValue($value)
{
if ($value === $this->hiddenPlaceholder) {
$value = $this->getLoadValue();
}
return $value;
}
/**
* @inheritDoc
*/
protected function loadAssets()
{
$this->addCss('css/sensitive.css', 'core');
$this->addJs('js/sensitive.js', 'core');
}
}

View File

@ -1,2 +0,0 @@
div[data-control="sensitive"] a[data-toggle],
div[data-control="sensitive"] a[data-copy] {box-shadow:none;border:1px solid #d1d6d9;border-left:0}

View File

@ -1,192 +0,0 @@
/*
* Sensitive field widget plugin.
*
* Data attributes:
* - data-control="sensitive" - enables the plugin on an element
*
* JavaScript API:
* $('div#someElement').sensitive({...})
*/
+function ($) { "use strict";
var Base = $.oc.foundation.base,
BaseProto = Base.prototype
var Sensitive = function(element, options) {
this.$el = $(element)
this.options = options
this.clean = Boolean(this.$el.data('clean'))
this.hidden = true
this.$input = this.$el.find('[data-input]').first()
this.$toggle = this.$el.find('[data-toggle]').first()
this.$icon = this.$el.find('[data-icon]').first()
this.$loader = this.$el.find('[data-loader]').first()
this.$copy = this.$el.find('[data-copy]').first()
$.oc.foundation.controlUtils.markDisposable(element)
Base.call(this)
this.init()
}
Sensitive.DEFAULTS = {
readOnly: false,
disabled: false,
eventHandler: null,
hideOnTabChange: false,
}
Sensitive.prototype = Object.create(BaseProto)
Sensitive.prototype.constructor = Sensitive
Sensitive.prototype.init = function() {
this.$input.on('keydown', this.proxy(this.onInput))
this.$toggle.on('click', this.proxy(this.onToggle))
if (this.options.hideOnTabChange) {
// Watch for tab change or minimise
document.addEventListener('visibilitychange', this.proxy(this.onTabChange))
}
if (this.$copy.length) {
this.$copy.on('click', this.proxy(this.onCopy))
}
}
Sensitive.prototype.dispose = function () {
this.$input.off('keydown', this.proxy(this.onInput))
this.$toggle.off('click', this.proxy(this.onToggle))
if (this.options.hideOnTabChange) {
document.removeEventListener('visibilitychange', this.proxy(this.onTabChange))
}
if (this.$copy.length) {
this.$copy.off('click', this.proxy(this.onCopy))
}
this.$input = this.$toggle = this.$icon = this.$loader = null
this.$el = null
BaseProto.dispose.call(this)
}
Sensitive.prototype.onInput = function() {
if (this.clean) {
this.clean = false
this.$input.val('')
}
return true
}
Sensitive.prototype.onToggle = function() {
if (this.$input.val() !== '' && this.clean) {
this.reveal()
} else {
this.toggleVisibility()
}
return true
}
Sensitive.prototype.onTabChange = function() {
if (document.hidden && !this.hidden) {
this.toggleVisibility()
}
}
Sensitive.prototype.onCopy = function() {
var that = this,
deferred = $.Deferred(),
isHidden = this.hidden
deferred.then(function () {
if (that.hidden) {
that.toggleVisibility()
}
that.$input.focus()
that.$input.select()
try {
document.execCommand('copy')
} catch (err) {
}
that.$input.blur()
if (isHidden) {
that.toggleVisibility()
}
})
if (this.$input.val() !== '' && this.clean) {
this.reveal(deferred)
} else {
deferred.resolve()
}
}
Sensitive.prototype.toggleVisibility = function() {
if (this.hidden) {
this.$input.attr('type', 'text')
} else {
this.$input.attr('type', 'password')
}
this.$icon.toggleClass('icon-eye icon-eye-slash')
this.hidden = !this.hidden
}
Sensitive.prototype.reveal = function(deferred) {
var that = this
this.$icon.css({
visibility: 'hidden'
})
this.$loader.removeClass('hide')
this.$input.request(this.options.eventHandler, {
success: function (data) {
that.$input.val(data.value)
that.clean = false
that.$icon.css({
visibility: 'visible'
})
that.$loader.addClass('hide')
that.toggleVisibility()
if (deferred) {
deferred.resolve()
}
}
})
}
var old = $.fn.sensitive
$.fn.sensitive = function (option) {
var args = Array.prototype.slice.call(arguments, 1), result
this.each(function () {
var $this = $(this)
var data = $this.data('oc.sensitive')
var options = $.extend({}, Sensitive.DEFAULTS, $this.data(), typeof option == 'object' && option)
if (!data) $this.data('oc.sensitive', (data = new Sensitive(this, options)))
if (typeof option == 'string') result = data[option].apply(data, args)
if (typeof result != 'undefined') return false
})
return result ? result : this
}
$.fn.sensitive.noConflict = function () {
$.fn.sensitive = old
return this
}
$(document).render(function () {
$('[data-control="sensitive"]').sensitive()
});
}(window.jQuery);

View File

@ -1,10 +0,0 @@
@import "../../../../assets/less/core/boot.less";
div[data-control="sensitive"] {
a[data-toggle],
a[data-copy] {
box-shadow: none;
border: 1px solid @input-group-addon-border-color;
border-left: 0;
}
}

View File

@ -1,41 +0,0 @@
<div
data-control="sensitive"
data-clean="true"
data-event-handler="<?= $this->getEventHandler('onShowValue') ?>"
<?php if ($hideOnTabChange): ?>data-hide-on-tab-change="true"<?php endif ?>
>
<div class="loading-indicator-container size-form-field">
<div class="input-group">
<input
type="password"
name="<?= $this->getFieldName() ?>"
id="<?= $this->getId() ?>"
value="<?= ($hasValue) ? $hiddenPlaceholder : '' ?>"
placeholder="<?= e(trans($this->formField->placeholder)) ?>"
class="form-control"
<?php if ($this->previewMode): ?>disabled="disabled"<?php endif ?>
autocomplete="off"
data-input
/>
<?php if ($allowCopy): ?>
<a
href="javascript:;"
class="input-group-addon btn btn-secondary"
data-copy
>
<i class="icon-copy"></i>
</a>
<?php endif ?>
<a
href="javascript:;"
class="input-group-addon btn btn-secondary"
data-toggle
>
<i class="icon-eye" data-icon></i>
</a>
</div>
<div class="loading-indicator hide" data-loader>
<span class="p-a"></span>
</div>
</div>
</div>

View File

@ -5,7 +5,7 @@ use Lang;
use Model;
use Response;
use League\Csv\Writer as CsvWriter;
use League\Csv\EscapeFormula as CsvEscapeFormula;
use October\Rain\Parse\League\EscapeFormula as CsvEscapeFormula;
use ApplicationException;
use SplTempFileObject;
@ -112,7 +112,8 @@ abstract class ExportModel extends Model
$csv->setEscape($options['escape']);
}
$csv->addFormatter(new CsvEscapeFormula());
// Temporary until upgrading to league/csv >= 9.1.0 (will be $csv->addFormatter($formatter))
$formatter = new CsvEscapeFormula();
/*
* Add headers
@ -127,6 +128,10 @@ abstract class ExportModel extends Model
*/
foreach ($results as $result) {
$data = $this->matchDataToColumns($result, $columns);
// Temporary until upgrading to league/csv >= 9.1.0
$data = $formatter($data);
$csv->insertOne($data);
}

View File

@ -5,7 +5,6 @@ use Str;
use Lang;
use Model;
use League\Csv\Reader as CsvReader;
use League\Csv\Statement as CsvStatement;
/**
* Model used for importing data
@ -109,6 +108,11 @@ abstract class ImportModel extends Model
*/
$reader = CsvReader::createFromPath($filePath, 'r');
// Filter out empty rows
$reader->addFilter(function (array $row) {
return count($row) > 1 || reset($row) !== null;
});
if ($options['delimiter'] !== null) {
$reader->setDelimiter($options['delimiter']);
}
@ -121,11 +125,15 @@ abstract class ImportModel extends Model
$reader->setEscape($options['escape']);
}
if ($options['firstRowTitles']) {
$reader->setOffset(1);
}
if (
$options['encoding'] !== null &&
$reader->supportsStreamFilter()
$reader->isActiveStreamFilter()
) {
$reader->addStreamFilter(sprintf(
$reader->appendStreamFilter(sprintf(
'%s%s:%s',
TranscodeFilter::FILTER_NAME,
strtolower($options['encoding']),
@ -133,19 +141,8 @@ abstract class ImportModel extends Model
));
}
// Create reader statement
$stmt = (new CsvStatement)
->where(function (array $row) {
// Filter out empty rows
return count($row) > 1 || reset($row) !== null;
});
if ($options['firstRowTitles']) {
$stmt = $stmt->offset(1);
}
$result = [];
$contents = $stmt->process($reader);
$contents = $reader->fetch();
foreach ($contents as $row) {
$result[] = $this->processImportRow($row, $matches);
}

View File

@ -27,8 +27,8 @@ class User extends UserBase
public $rules = [
'email' => 'required|between:6,255|email|unique:backend_users',
'login' => 'required|between:2,255|unique:backend_users',
'password' => 'required:create|min:4|confirmed',
'password_confirmation' => 'required_with:password|min:4'
'password' => 'required:create|between:4,255|confirmed',
'password_confirmation' => 'required_with:password|between:4,255'
];
/**

View File

@ -25,7 +25,7 @@ App::before(function ($request) {
'middleware' => ['web'],
'prefix' => Config::get('cms.backendUri', 'backend')
], function () {
Route::any('{slug?}', 'Backend\Classes\BackendController@run')->where('slug', '(.*)?');
Route::any('{slug}', 'Backend\Classes\BackendController@run')->where('slug', '(.*)?');
})
;

View File

@ -287,14 +287,25 @@ class Asset extends Extendable
$directory = $this->theme->getPath() . '/' . $this->dirName . '/';
$filePath = $directory . $fileName;
$resolvedPath = resolve_path($filePath);
$path = realpath($filePath);
/**
* If the path doesn't exist yet, then create it temporarily
* in order to run realpath() resolution on it to verify the
* final destination and then remove the temporary file.
*/
if (!$path) {
touch($filePath);
$path = realpath($filePath);
unlink($filePath);
}
// Limit paths to those under the theme's assets directory
if (!starts_with($resolvedPath, $directory)) {
if (!starts_with($path, $directory)) {
return false;
}
return $resolvedPath;
return $path;
}
/**

View File

@ -316,8 +316,7 @@ class CmsCompoundObject extends CmsObject
self::$objectComponentPropertyMap = $objectComponentMap;
$expiresAt = now()->addMinutes(Config::get('cms.parsedPageCacheTTL', 10));
Cache::put($key, base64_encode(serialize($objectComponentMap)), $expiresAt);
Cache::put($key, base64_encode(serialize($objectComponentMap)), Config::get('cms.parsedPageCacheTTL', 10));
if (array_key_exists($componentName, $objectComponentMap[$objectCode])) {
return $objectComponentMap[$objectCode][$componentName];

View File

@ -227,16 +227,7 @@ class CmsObject extends HalcyonModel implements CmsObjectContract
$fileName = $this->fileName;
}
$directory = $this->theme->getPath() . '/' . $this->getObjectTypeDirName() . '/';
$filePath = $directory . $fileName;
$resolvedPath = resolve_path($filePath);
// Limit paths to those under the corresponding theme directory
if (!starts_with($resolvedPath, $directory)) {
return false;
}
return $resolvedPath;
return $this->theme->getPath().'/'.$this->getObjectTypeDirName().'/'.$fileName;
}
/**

View File

@ -1,6 +1,5 @@
<?php namespace Cms\Classes;
use ApplicationException;
use October\Rain\Support\Collection as CollectionBase;
/**
@ -38,32 +37,15 @@ class CmsObjectCollection extends CollectionBase
/**
* Returns objects whose properties match the supplied value.
*
* Note that this deviates from Laravel 6's Illuminate\Support\Traits\EnumeratesValues::where() method signature,
* which uses ($key, $operator = null, $value = null) as parameters and that this class extends.
*
* To ensure backwards compatibility with our current Halcyon functionality, this method retains the original
* parameters and functions the same way as before, with handling for the $value and $strict parameters to ensure
* they match the previously expected formats. This means that you cannot use operators for "where" queries on
* CMS object collections.
*
* @param string $property
* @param string $value
* @param bool $strict
* @param string $property
* @param string $value
* @param bool $strict
* @return static
*/
public function where($property, $value = null, $strict = null)
public function where($property, $value, $strict = true)
{
if (empty($value) || !is_string($value)) {
throw new ApplicationException('You must provide a string value to compare with when executing a "where" '
. 'query for CMS object collections.');
}
if (!isset($strict) || !is_bool($strict)) {
$strict = true;
}
return $this->filter(function ($object) use ($property, $value, $strict) {
if (!array_key_exists($property, $object->settings)) {
return false;
}

View File

@ -224,8 +224,7 @@ class CodeParser
$cached = $this->getCachedInfo() ?: [];
$cached[$this->filePath] = $cacheItem;
$expiresAt = now()->addMinutes(1440);
Cache::put($this->dataCacheKey, base64_encode(serialize($cached)), $expiresAt);
Cache::put($this->dataCacheKey, base64_encode(serialize($cached)), 1440);
self::$cache[$this->filePath] = $result;
}

View File

@ -127,11 +127,10 @@ class Router
: $fileName;
$key = $this->getUrlListCacheKey();
$expiresAt = now()->addMinutes(Config::get('cms.urlCacheTtl', 1));
Cache::put(
$key,
base64_encode(serialize($urlList)),
$expiresAt
Config::get('cms.urlCacheTtl', 1)
);
}
}
@ -252,8 +251,7 @@ class Router
$this->urlMap = $map;
if ($cacheable) {
$expiresAt = now()->addMinutes(Config::get('cms.urlCacheTtl', 1));
Cache::put($key, base64_encode(serialize($map)), $expiresAt);
Cache::put($key, base64_encode(serialize($map)), Config::get('cms.urlCacheTtl', 1));
}
return false;

View File

@ -158,8 +158,7 @@ class Theme
if ($checkDatabase && App::hasDatabase()) {
try {
try {
$expiresAt = now()->addMinutes(1440);
$dbResult = Cache::remember(self::ACTIVE_KEY, $expiresAt, function () {
$dbResult = Cache::remember(self::ACTIVE_KEY, 1440, function () {
return Parameter::applyKey(self::ACTIVE_KEY)->value('value');
});
}

View File

@ -8,13 +8,11 @@
"authors": [
{
"name": "Alexey Bobkov",
"email": "aleksey.bobkov@gmail.com",
"role": "Co-founder"
"email": "aleksey.bobkov@gmail.com"
},
{
"name": "Samuel Georges",
"email": "daftspunky@gmail.com",
"role": "Co-founder"
"email": "daftspunky@gmail.com"
},
{
"name": "Luke Towers",
@ -24,10 +22,9 @@
}
],
"require": {
"php": ">=7.2",
"php": ">=7.0",
"composer/installers": "~1.0",
"october/rain": "~1.0",
"laravel/framework": "~6.0"
"october/rain": "~1.0"
},
"autoload": {
"psr-4": {

View File

@ -22,7 +22,7 @@ App::before(function ($request) {
* The CMS module intercepts all URLs that were not
* handled by the back-end modules.
*/
Route::any('{slug?}', 'Cms\Classes\CmsController@run')->where('slug', '(.*)?')->middleware('web');
Route::any('{slug}', 'Cms\Classes\CmsController@run')->where('slug', '(.*)?')->middleware('web');
/**
* @event cms.route

View File

@ -190,8 +190,7 @@ trait UrlMaker
'mtime' => @File::lastModified($filePath)
];
$expiresAt = now()->addMinutes(Config::get('cms.parsedPageCacheTTL', 1440));
Cache::put($key, serialize($cached), $expiresAt);
Cache::put($key, serialize($cached), Config::get('cms.parsedPageCacheTTL', 1440));
return static::$urlPageName = $baseFileName;
}

View File

@ -8,7 +8,7 @@ use Cms\Classes\Controller;
use Cms\Classes\ComponentBase;
use Illuminate\Pagination\Paginator;
use Illuminate\Support\Collection;
use Symfony\Component\VarDumper\Dumper\HtmlDumper;
use Illuminate\Support\Debug\HtmlDumper;
use Symfony\Component\VarDumper\Cloner\VarCloner;
use October\Rain\Database\Model;

View File

@ -94,7 +94,6 @@ class ServiceProvider extends ModuleServiceProvider
}
}
Paginator::useBootstrapThree();
Paginator::defaultSimpleView('system::pagination.simple-default');
/*

View File

@ -16,6 +16,7 @@ return [
'Eloquent' => Illuminate\Database\Eloquent\Model::class,
'Event' => Illuminate\Support\Facades\Event::class,
'Hash' => Illuminate\Support\Facades\Hash::class,
'Input' => Illuminate\Support\Facades\Input::class,
'Lang' => Illuminate\Support\Facades\Lang::class,
'Log' => Illuminate\Support\Facades\Log::class,
'Mail' => Illuminate\Support\Facades\Mail::class,
@ -29,6 +30,7 @@ return [
'Storage' => Illuminate\Support\Facades\Storage::class,
'Url' => Illuminate\Support\Facades\URL::class, // Preferred
'URL' => Illuminate\Support\Facades\URL::class,
'Validator' => Illuminate\Support\Facades\Validator::class,
'View' => Illuminate\Support\Facades\View::class,
/*
@ -40,7 +42,6 @@ return [
'Config' => October\Rain\Support\Facades\Config::class,
'Seeder' => October\Rain\Database\Updates\Seeder::class,
'Flash' => October\Rain\Support\Facades\Flash::class,
'Input' => October\Rain\Support\Facades\Input::class,
'Form' => October\Rain\Support\Facades\Form::class,
'Html' => October\Rain\Support\Facades\Html::class,
'Http' => October\Rain\Support\Facades\Http::class,
@ -51,7 +52,6 @@ return [
'Twig' => October\Rain\Support\Facades\Twig::class,
'DbDongle' => October\Rain\Support\Facades\DbDongle::class,
'Schema' => October\Rain\Support\Facades\Schema::class,
'Validator' => October\Rain\Support\Facades\Validator::class,
'Cms' => Cms\Facades\Cms::class,
'Backend' => Backend\Facades\Backend::class,
'BackendMenu' => Backend\Facades\BackendMenu::class,
@ -60,12 +60,4 @@ return [
'SystemException' => October\Rain\Exception\SystemException::class,
'ApplicationException' => October\Rain\Exception\ApplicationException::class,
'ValidationException' => October\Rain\Exception\ValidationException::class,
/*
* Fallback aliases
*/
// Input facade was removed in Laravel 6 - we are keeping it in the Rain library for backwards compatibility.
'Illuminate\Support\Facades\Input' => October\Rain\Support\Facades\Input::class,
// Illuminate's HtmlDumper was "dumped" in Laravel 6 - we'll route this to Symfony's HtmlDumper as Laravel have done.
'Illuminate\Support\Debug\HtmlDumper' => Symfony\Component\VarDumper\Dumper\HtmlDumper::class,
];

View File

@ -10,11 +10,11 @@ use Route;
use Config;
use Request;
use Response;
use October\Rain\Assetic\Asset\FileAsset;
use October\Rain\Assetic\Asset\AssetCache;
use October\Rain\Assetic\Asset\AssetCollection;
use October\Rain\Assetic\Cache\FilesystemCache;
use October\Rain\Assetic\Factory\AssetFactory;
use Assetic\Asset\FileAsset;
use Assetic\Asset\AssetCache;
use Assetic\Asset\AssetCollection;
use Assetic\Factory\AssetFactory;
use October\Rain\Parse\Assetic\FilesystemCache;
use System\Helpers\Cache as CacheHelper;
use ApplicationException;
use DateTime;
@ -126,22 +126,22 @@ class CombineAssets
/*
* Register JavaScript filters
*/
$this->registerFilter('js', new \October\Rain\Assetic\Filter\JavascriptImporter);
$this->registerFilter('js', new \October\Rain\Parse\Assetic\JavascriptImporter);
/*
* Register CSS filters
*/
$this->registerFilter('css', new \October\Rain\Assetic\Filter\CssImportFilter);
$this->registerFilter(['css', 'less', 'scss'], new \October\Rain\Assetic\Filter\CssRewriteFilter);
$this->registerFilter('less', new \October\Rain\Assetic\Filter\LessCompiler);
$this->registerFilter('scss', new \October\Rain\Assetic\Filter\ScssCompiler);
$this->registerFilter('css', new \Assetic\Filter\CssImportFilter);
$this->registerFilter(['css', 'less', 'scss'], new \Assetic\Filter\CssRewriteFilter);
$this->registerFilter('less', new \October\Rain\Parse\Assetic\LessCompiler);
$this->registerFilter('scss', new \October\Rain\Parse\Assetic\ScssCompiler);
/*
* Minification filters
*/
if ($this->useMinify) {
$this->registerFilter('js', new \October\Rain\Assetic\Filter\JSMinFilter);
$this->registerFilter(['css', 'less', 'scss'], new \October\Rain\Assetic\Filter\StylesheetMinify);
$this->registerFilter('js', new \Assetic\Filter\JSMinFilter);
$this->registerFilter(['css', 'less', 'scss'], new \October\Rain\Parse\Assetic\StylesheetMinify);
}
/*

View File

@ -134,11 +134,10 @@ class MediaLibrary
$folderContents = $this->scanFolderContents($fullFolderPath);
$cached[$fullFolderPath] = $folderContents;
$expiresAt = now()->addMinutes(Config::get('cms.storage.media.ttl', 10));
Cache::put(
$this->cacheKey,
base64_encode(serialize($cached)),
$expiresAt
Config::get('cms.storage.media.ttl', 10)
);
}

View File

@ -29,6 +29,11 @@ class UpdateManager
{
use \October\Rain\Support\Traits\Singleton;
/**
* @var array The notes for the current operation.
*/
protected $notes = [];
/**
* @var \Illuminate\Console\OutputStyle
*/
@ -340,13 +345,13 @@ class UpdateManager
/*
* Rollback modules
*/
if (isset($this->notesOutput)) {
$this->migrator->setOutput($this->notesOutput);
}
while (true) {
$rolledBack = $this->migrator->rollback($paths, ['pretend' => false]);
foreach ($this->migrator->getNotes() as $note) {
$this->note($note);
}
if (count($rolledBack) == 0) {
break;
}
@ -398,13 +403,13 @@ class UpdateManager
*/
public function migrateModule($module)
{
if (isset($this->notesOutput)) {
$this->migrator->setOutput($this->notesOutput);
}
$this->migrator->run(base_path() . '/modules/' . strtolower($module) . '/database/migrations');
$this->note($module);
$this->migrator->run(base_path() . '/modules/'.strtolower($module).'/database/migrations');
foreach ($this->migrator->getNotes() as $note) {
$this->note(' - ' . $note);
}
return $this;
}
@ -513,9 +518,13 @@ class UpdateManager
$this->note($name);
$this->versionManager->setNotesOutput($this->notesOutput);
$this->versionManager->resetNotes()->setNotesOutput($this->notesOutput);
$this->versionManager->updatePlugin($plugin);
if ($this->versionManager->updatePlugin($plugin) !== false) {
foreach ($this->versionManager->getNotes() as $note) {
$this->note($note);
}
}
return $this;
}
@ -704,8 +713,7 @@ class UpdateManager
}
$data = $this->requestServerData($type . '/popular');
$expiresAt = now()->addMinutes(60);
Cache::put($cacheKey, base64_encode(serialize($data)), $expiresAt);
Cache::put($cacheKey, base64_encode(serialize($data)), 60);
foreach ($data as $product) {
$code = array_get($product, 'code', -1);
@ -794,11 +802,35 @@ class UpdateManager
{
if ($this->notesOutput !== null) {
$this->notesOutput->writeln($message);
} else {
$this->notes[] = $message;
}
return $this;
}
/**
* Get the notes for the last operation.
* @return array
*/
public function getNotes()
{
return $this->notes;
}
/**
* Resets the notes store.
* @return self
*/
public function resetNotes()
{
$this->notesOutput = null;
$this->notes = [];
return $this;
}
/**
* Sets an output stream for writing notes.
* @param Illuminate\Console\Command $output

View File

@ -29,6 +29,12 @@ class VersionManager
const HISTORY_TYPE_COMMENT = 'comment';
const HISTORY_TYPE_SCRIPT = 'script';
/**
* The notes for the current operation.
* @var array
*/
protected $notes = [];
/**
* @var \Illuminate\Console\OutputStyle
*/
@ -420,7 +426,6 @@ class VersionManager
* Execute the database PHP script
*/
$updateFile = $this->pluginManager->getPluginPath($code) . '/updates/' . $script;
$this->updater->packDown($updateFile);
Db::table('system_plugin_history')
@ -503,11 +508,35 @@ class VersionManager
{
if ($this->notesOutput !== null) {
$this->notesOutput->writeln($message);
} else {
$this->notes[] = $message;
}
return $this;
}
/**
* Get the notes for the last operation.
* @return array
*/
public function getNotes()
{
return $this->notes;
}
/**
* Resets the notes store.
* @return self
*/
public function resetNotes()
{
$this->notesOutput = null;
$this->notes = [];
return $this;
}
/**
* Sets an output stream for writing notes.
* @param Illuminate\Console\Command $output
@ -521,7 +550,8 @@ class VersionManager
}
/**
* Extract script and comments from version details
* @param $details
*
* @return array
*/
protected function extractScriptsAndComments($details): array
@ -536,8 +566,7 @@ class VersionManager
$scripts = array_values(array_filter($details, function ($detail) use ($fileNamePattern) {
return preg_match($fileNamePattern, $detail);
}));
}
else {
} else {
$comments = (array)$details;
$scripts = [];
}

View File

@ -8,13 +8,11 @@
"authors": [
{
"name": "Alexey Bobkov",
"email": "aleksey.bobkov@gmail.com",
"role": "Co-founder"
"email": "aleksey.bobkov@gmail.com"
},
{
"name": "Samuel Georges",
"email": "daftspunky@gmail.com",
"role": "Co-founder"
"email": "daftspunky@gmail.com"
},
{
"name": "Luke Towers",
@ -24,10 +22,9 @@
}
],
"require": {
"php": ">=7.2",
"php": ">=7.0",
"composer/installers": "~1.0",
"october/rain": "~1.0",
"laravel/framework": "~6.0"
"october/rain": "~1.0"
},
"autoload": {
"psr-4": {

View File

@ -369,7 +369,7 @@ class OctoberEnv extends Command
'SESSION_DRIVER' => 'driver',
],
'queue' => [
'QUEUE_CONNECTION' => 'default',
'QUEUE_DRIVER' => 'default',
],
'mail' => [
'MAIL_DRIVER' => 'driver',

View File

@ -17,6 +17,7 @@ use Symfony\Component\Console\Input\InputOption;
*/
class OctoberUpdate extends Command
{
/**
* The console command name.
*/

View File

@ -2,8 +2,6 @@
use Lang;
use Flash;
use Config;
use Request;
use Backend;
use BackendMenu;
use System\Classes\SettingsManager;
@ -141,22 +139,6 @@ class Settings extends Controller
return $this->formWidget->render($options);
}
/**
* Returns the form widget used by this behavior.
*
* @return \Backend\Widgets\Form
*/
public function formGetWidget()
{
if (is_null($this->formWidget)) {
$item = $this->findSettingItem();
$model = $this->createModel($item);
$this->initWidgets($model);
}
return $this->formWidget;
}
/**
* Prepare the widgets used by this action
* Model $model
@ -187,22 +169,10 @@ class Settings extends Controller
}
/**
* Locates a setting item for a module or plugin.
*
* If none of the parameters are provided, they will be auto-guessed from the URL.
*
* @param string|null $author
* @param string|null $plugin
* @param string|null $code
*
* @return array
* Locates a setting item for a module or plugin
*/
protected function findSettingItem($author = null, $plugin = null, $code = null)
protected function findSettingItem($author, $plugin, $code)
{
if (is_null($author) || is_null($plugin)) {
[$author, $plugin, $code] = $this->guessSettingItem();
}
$manager = SettingsManager::instance();
$moduleOwner = $author;
@ -217,23 +187,4 @@ class Settings extends Controller
return $item;
}
/**
* Guesses the requested setting item from the current URL segments provided by the Request object.
*
* @return array
*/
protected function guessSettingItem()
{
$segments = Request::segments();
if (!empty(Config::get('cms.backendUri', 'backend'))) {
array_splice($segments, 0, 4);
} else {
array_splice($segments, 0, 3);
}
// Ensure there's at least 3 segments
return array_pad($segments, 3, null);
}
}

View File

@ -13,8 +13,8 @@ class DatabaseSeeder extends Seeder
*/
public function run()
{
Eloquent::unguarded(function () {
$this->call('System\Database\Seeds\SeedSetupMailLayouts');
});
Eloquent::unguard();
$this->call('System\Database\Seeds\SeedSetupMailLayouts');
}
}

View File

@ -32,7 +32,6 @@ return [
'boolean' => 'The :attribute field must be true or false.',
'confirmed' => 'The :attribute confirmation does not match.',
'date' => 'The :attribute is not a valid date.',
'date_equals' => 'The :attribute must be a date equal to :date.',
'date_format' => 'The :attribute does not match the format :format.',
'different' => 'The :attribute and :other must be different.',
'digits' => 'The :attribute must be :digits digits.',
@ -40,22 +39,9 @@ return [
'dimensions' => 'The :attribute has invalid image dimensions.',
'distinct' => 'The :attribute field has a duplicate value.',
'email' => 'The :attribute must be a valid email address.',
'ends_with' => 'The :attribute must end with one of the following: :values.',
'exists' => 'The selected :attribute is invalid.',
'file' => 'The :attribute must be a file.',
'filled' => 'The :attribute field must have a value.',
'gt' => [
'numeric' => 'The :attribute must be greater than :value.',
'file' => 'The :attribute must be greater than :value kilobytes.',
'string' => 'The :attribute must be greater than :value characters.',
'array' => 'The :attribute must have more than :value items.',
],
'gte' => [
'numeric' => 'The :attribute must be greater than or equal :value.',
'file' => 'The :attribute must be greater than or equal :value kilobytes.',
'string' => 'The :attribute must be greater than or equal :value characters.',
'array' => 'The :attribute must have :value items or more.',
],
'image' => 'The :attribute must be an image.',
'in' => 'The selected :attribute is invalid.',
'in_array' => 'The :attribute field does not exist in :other.',
@ -64,18 +50,6 @@ return [
'ipv4' => 'The :attribute must be a valid IPv4 address.',
'ipv6' => 'The :attribute must be a valid IPv6 address.',
'json' => 'The :attribute must be a valid JSON string.',
'lt' => [
'numeric' => 'The :attribute must be less than :value.',
'file' => 'The :attribute must be less than :value kilobytes.',
'string' => 'The :attribute must be less than :value characters.',
'array' => 'The :attribute must have less than :value items.',
],
'lte' => [
'numeric' => 'The :attribute must be less than or equal :value.',
'file' => 'The :attribute must be less than or equal :value kilobytes.',
'string' => 'The :attribute must be less than or equal :value characters.',
'array' => 'The :attribute must not have more than :value items.',
],
'max' => [
'numeric' => 'The :attribute may not be greater than :max.',
'file' => 'The :attribute may not be greater than :max kilobytes.',
@ -91,7 +65,6 @@ return [
'array' => 'The :attribute must have at least :min items.',
],
'not_in' => 'The selected :attribute is invalid.',
'not_regex' => 'The :attribute format is invalid.',
'numeric' => 'The :attribute must be a number.',
'present' => 'The :attribute field must be present.',
'regex' => 'The :attribute format is invalid.',
@ -109,13 +82,11 @@ return [
'string' => 'The :attribute must be :size characters.',
'array' => 'The :attribute must contain :size items.',
],
'starts_with' => 'The :attribute must start with one of the following: :values.',
'string' => 'The :attribute must be a string.',
'timezone' => 'The :attribute must be a valid zone.',
'unique' => 'The :attribute has already been taken.',
'uploaded' => 'The :attribute failed to upload.',
'url' => 'The :attribute format is invalid.',
'uuid' => 'The :attribute must be a valid UUID.',
/*
|--------------------------------------------------------------------------

View File

@ -79,7 +79,6 @@ tabs:
smtp_password:
label: system::lang.mail.smtp_password
tab: system::lang.mail.general
type: sensitive
span: right
trigger:
action: show
@ -108,7 +107,6 @@ tabs:
label: system::lang.mail.mailgun_secret
commentAbove: system::lang.mail.mailgun_secret_comment
tab: system::lang.mail.general
type: sensitive
trigger:
action: show
field: send_mode
@ -118,7 +116,6 @@ tabs:
label: system::lang.mail.mandrill_secret
commentAbove: system::lang.mail.mandrill_secret_comment
tab: system::lang.mail.general
type: sensitive
trigger:
action: show
field: send_mode
@ -138,7 +135,6 @@ tabs:
label: system::lang.mail.ses_secret
commentAbove: system::lang.mail.ses_secret_comment
tab: system::lang.mail.general
type: sensitive
span: right
trigger:
action: show
@ -158,7 +154,6 @@ tabs:
sparkpost_secret:
label: system::lang.mail.sparkpost_secret
commentAbove: system::lang.mail.sparkpost_secret_comment
type: sensitive
tab: system::lang.mail.general
trigger:
action: show

View File

@ -15,7 +15,9 @@ return [
Illuminate\Pagination\PaginationServiceProvider::class,
Illuminate\Pipeline\PipelineServiceProvider::class,
Illuminate\Queue\QueueServiceProvider::class,
Illuminate\Redis\RedisServiceProvider::class,
Illuminate\Session\SessionServiceProvider::class,
Illuminate\Validation\ValidationServiceProvider::class,
Illuminate\View\ViewServiceProvider::class,
Laravel\Tinker\TinkerServiceProvider::class,
@ -34,7 +36,5 @@ return [
October\Rain\Flash\FlashServiceProvider::class,
October\Rain\Mail\MailServiceProvider::class,
October\Rain\Argon\ArgonServiceProvider::class,
October\Rain\Redis\RedisServiceProvider::class,
October\Rain\Validation\ValidationServiceProvider::class,
];

View File

@ -8,6 +8,7 @@
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="false"
syntaxCheck="false"
>
<testsuites>
<testsuite name="October CMS Test Suite">

View File

@ -28,7 +28,7 @@ abstract class PluginTestCase extends TestCase
$app['cache']->setDefaultDriver('array');
$app->setLocale('en');
$app->singleton('backend.auth', function ($app) {
$app->singleton('auth', function ($app) {
$app['auth.loaded'] = true;
return AuthManager::instance();
@ -67,7 +67,7 @@ abstract class PluginTestCase extends TestCase
* Perform test case set up.
* @return void
*/
public function setUp() : void
public function setUp()
{
/*
* Force reload of October singletons
@ -105,7 +105,7 @@ abstract class PluginTestCase extends TestCase
* Flush event listeners and collect garbage.
* @return void
*/
public function tearDown() : void
public function tearDown()
{
$this->flushModelEventListeners();
parent::tearDown();

View File

@ -1,6 +1,6 @@
# Plugin testing
Individual plugin test cases can be run by running `../../../vendor/bin/phpunit` in the plugin's base directory (ex. `plugins/acme/demo`.
Plugin unit tests can be performed by running `phpunit` in the base plugin directory.
### Creating plugin tests
@ -58,7 +58,7 @@ The test class should extend the base class `PluginTestCase` and this is a speci
class BaseTestCase extends PluginTestCase
{
public function setUp(): void
public function setUp()
{
parent::setUp();
@ -72,7 +72,7 @@ The test class should extend the base class `PluginTestCase` and this is a speci
$pluginManager->bootAll(true);
}
public function tearDown(): void
public function tearDown()
{
parent::tearDown();
@ -96,10 +96,39 @@ To perform unit testing on the core October files, you should download a develop
### Unit tests
Unit tests can be performed by running `vendor/bin/phpunit` in the root directory of your October CMS installation.
Unit tests can be performed by running `phpunit` in the root directory or inside `/tests/unit`.
### Functional tests
Functional tests can be performed by installing the [RainLab Dusk](https://octobercms.com/plugin/rainlab-dusk) in your October CMS installation. The RainLab Dusk plugin is powered by Laravel Dusk, a comprehensive testing suite for the Laravel framework that is designed to test interactions with a fully operational October CMS instance through a virtual browser.
Functional tests can be performed by running `phpunit` in the `/tests/functional` directory. Ensure the following configuration is met:
For information on installing and setting up your October CMS install to run functional tests, please review the [README](https://github.com/rainlab/dusk-plugin/blob/master/README.md) for the plugin.
- Active theme is `demo`
- Language preference is `en`
#### Selenium set up
1. Download latest Java SE from http://java.sun.com/ and install
1. Download a distribution archive of [Selenium Server](http://seleniumhq.org/download/).
1. Unzip the distribution archive and copy selenium-server-standalone-2.42.2.jar (check the version suffix) to /usr/local/bin, for instance.
1. Start the Selenium Server server by running `java -jar /usr/local/bin/selenium-server-standalone-2.42.2.jar`.
#### Selenium configuration
Create a new file `selenium.php` in the root directory, add the following content:
<?php
// Selenium server details
define('TEST_SELENIUM_HOST', '127.0.0.1');
define('TEST_SELENIUM_PORT', 4444);
define('TEST_SELENIUM_BROWSER', '*firefox');
// Back-end URL
define('TEST_SELENIUM_URL', 'http://localhost/backend/');
// Active Theme
define('TEST_SELENIUM_THEME', 'demo');
// Back-end credentials
define('TEST_SELENIUM_USER', 'admin');
define('TEST_SELENIUM_PASS', 'admin');

111
tests/UiTestCase.php Normal file
View File

@ -0,0 +1,111 @@
<?php
class UiTestCase extends PHPUnit_Extensions_SeleniumTestCase
{
protected function setUp()
{
/*
* Look for selenium configuration
*/
if (file_exists($seleniumEnv = __DIR__.'/../selenium.php')) {
require_once $seleniumEnv;
}
/*
* Configure selenium
*/
if (!defined('TEST_SELENIUM_URL')) {
return $this->markTestSkipped('Selenium skipped');
}
if (defined('TEST_SELENIUM_HOST')) {
$this->setHost(TEST_SELENIUM_HOST);
}
if (defined('TEST_SELENIUM_PORT')) {
$this->setPort(TEST_SELENIUM_PORT);
}
if (defined('TEST_SELENIUM_BROWSER')) {
$this->setBrowser(TEST_SELENIUM_BROWSER);
}
$this->setBrowserUrl(TEST_SELENIUM_URL);
}
//
// OctoberCMS Helpers
//
protected function signInToBackend()
{
$this->open('backend');
$this->type("name=login", TEST_SELENIUM_USER);
$this->type("name=password", TEST_SELENIUM_PASS);
$this->click("//button[@type='submit']");
$this->waitForPageToLoad("30000");
}
/**
* Similar to the native getConfirmation() function
*/
protected function getSweetConfirmation($expectedText = null, $clickOk = true)
{
$this->waitForElementPresent("xpath=(//div[@class='sweet-alert showSweetAlert visible'])[1]");
if ($expectedText) {
$this->verifyText("//div[@class='sweet-alert showSweetAlert visible']//h4", $expectedText);
}
$this->verifyText("//div[@class='sweet-alert showSweetAlert visible']//button[@class='confirm btn btn-primary']", "OK");
if ($clickOk) {
$this->click("xpath=(//div[@class='sweet-alert showSweetAlert visible']//button[@class='confirm btn btn-primary'])[1]");
}
}
//
// Selenium helpers
//
protected function waitForElementPresent($target, $timeout = 60)
{
$second = 0;
while (true) {
if ($second >= $timeout) {
$this->fail('timeout');
}
try {
if ($this->isElementPresent($target)) {
break;
}
}
catch (Exception $e) {
}
sleep(1);
++$second;
}
}
protected function waitForElementNotPresent($target, $timeout = 60)
{
$second = 0;
while (true) {
if ($second >= $timeout) {
$this->fail('timeout');
}
try {
if (!$this->isElementPresent($target)) {
break;
}
}
catch (Exception $e) {
}
sleep(1);
++$second;
}
}
}

View File

@ -19,3 +19,14 @@ $loader->addDirectories([
'modules',
'plugins'
]);
/*
* Monkey patch PHPUnit\Framework\MockObject\Generator to avoid
* "Function ReflectionType::__toString() is deprecated" warnings
*/
$generatorPatchPath = __DIR__ . '/resources/patches/php-generator-7.php';
$generatorSourcePath = __DIR__ . '/../vendor/phpunit/phpunit-mock-objects/src/Generator.php';
if (file_exists($generatorSourcePath)) {
file_put_contents($generatorSourcePath, file_get_contents($generatorPatchPath));
}

View File

@ -29,7 +29,7 @@ trait InteractsWithAuthentication
*/
public function be(UserContract $user, $driver = null)
{
$this->app['backend.auth']->setUser($user);
$this->app['auth']->setUser($user);
}
/**
@ -66,7 +66,7 @@ trait InteractsWithAuthentication
*/
protected function isAuthenticated($guard = null)
{
return $this->app->make('backend.auth')->guard($guard)->check();
return $this->app->make('auth')->guard($guard)->check();
}
/**
@ -78,7 +78,7 @@ trait InteractsWithAuthentication
*/
public function assertAuthenticatedAs($user, $guard = null)
{
$expected = $this->app->make('backend.auth')->guard($guard)->user();
$expected = $this->app->make('auth')->guard($guard)->user();
$this->assertNotNull($expected, 'The current user is not authenticated.');
@ -140,7 +140,7 @@ trait InteractsWithAuthentication
*/
protected function hasCredentials(array $credentials, $guard = null)
{
$provider = $this->app->make('backend.auth')->guard($guard)->getProvider();
$provider = $this->app->make('auth')->guard($guard)->getProvider();
$user = $provider->retrieveByCredentials($credentials);

View File

@ -20,7 +20,6 @@ class Author extends Model
*/
public $belongsTo = [
'user' => ['Database\Tester\Models\User', 'delete' => true],
'country' => ['Database\Tester\Models\Country'],
'user_soft' => ['Database\Tester\Models\SoftDeleteUser', 'key' => 'user_id', 'softDelete' => true],
];

View File

@ -1,35 +0,0 @@
<?php namespace Database\Tester\Models;
use Model;
class Country extends Model
{
/**
* @var string The database table used by the model.
*/
public $table = 'database_tester_countries';
/**
* @var array Guarded fields
*/
protected $guarded = [];
public $hasMany = [
'users' => [
'Database\Tester\Models\User',
],
];
public $hasManyThrough = [
'posts' => [
'Database\Tester\Models\Post',
'through' => 'Database\Tester\Models\Author',
]
];
}
class SoftDeleteCountry extends Country
{
use \October\Rain\Database\Traits\SoftDelete;
}

View File

@ -17,19 +17,6 @@ class User extends Model
/**
* @var array Relations
*/
public $hasOne = [
'author' => [
'Database\Tester\Models\Author',
]
];
public $hasOneThrough = [
'phone' => [
'Database\Tester\Models\Phone',
'through' => 'Database\Tester\Models\Author',
],
];
public $attachOne = [
'avatar' => 'System\Models\File'
];

View File

@ -11,7 +11,6 @@ class CreateAuthorsTable extends Migration
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user_id')->unsigned()->index()->nullable();
$table->integer('country_id')->unsigned()->index()->nullable();
$table->string('name')->nullable();
$table->string('email')->nullable();
$table->softDeletes();

View File

@ -1,23 +0,0 @@
<?php namespace Database\Tester\Updates;
use Schema;
use October\Rain\Database\Updates\Migration;
class CreateCountriesTable extends Migration
{
public function up()
{
Schema::create('database_tester_countries', function ($table) {
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('name')->nullable();
$table->softDeletes();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('database_tester_countries');
}
}

View File

@ -9,4 +9,3 @@
- create_users_table.php
- create_event_log_table.php
- create_meta_table.php
- create_countries_table.php

View File

@ -1 +0,0 @@
console.log('script1.js');

View File

@ -1 +0,0 @@
console.log('script2.js');

View File

@ -1 +0,0 @@
console.log('subdir/script1.js');

View File

@ -0,0 +1,90 @@
<?php
class AuthTest extends UiTestCase
{
public function testSignInAndOut()
{
$this->open('backend');
$cssLogoutLink = '#layout-mainmenu .mainmenu-accountmenu > ul > li:first-child > a';
try {
$this->assertTitle('Administration Area');
$this->assertTrue($this->isElementPresent("name=login"));
$this->assertTrue($this->isElementPresent("name=password"));
$this->assertTrue($this->isElementPresent("//button[@type='submit']"));
$this->verifyText("//button[@type='submit']", "Login");
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
/*
* Sign in
*/
$this->type("name=login", TEST_SELENIUM_USER);
$this->type("name=password", TEST_SELENIUM_PASS);
$this->click("//button[@type='submit']");
$this->waitForPageToLoad("30000");
try {
$this->assertTitle('Dashboard | October CMS');
$this->assertTrue($this->isElementPresent('css='.$cssLogoutLink));
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
$this->verifyText('css='.$cssLogoutLink, "Sign out");
/*
* Log out
*/
$this->click('css='.$cssLogoutLink);
$this->waitForPageToLoad("30000");
try {
$this->assertTitle('Administration Area');
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
}
public function testPasswordReset()
{
$this->open('backend');
try {
$this->assertTrue($this->isElementPresent("link=exact:Forgot your password?"));
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
$this->click('link=exact:Forgot your password?');
$this->waitForPageToLoad("30000");
try {
$this->assertTrue($this->isElementPresent("//button[@type='submit']"));
$this->verifyText("//button[@type='submit']", "Restore");
$this->assertTrue($this->isElementPresent("link=Cancel"));
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
$this->type("name=login", TEST_SELENIUM_USER);
sleep(1);
$this->click("//button[@type='submit']");
$this->waitForPageToLoad("30000");
try {
$this->assertTitle('Administration Area');
$this->assertTrue($this->isElementPresent("css=p.flash-message.success"));
$this->verifyText("css=p.flash-message.success", "An email has been sent to your email address with password restore instructions.×");
}
catch (PHPUnit_Framework_AssertionFailedError $e) {
array_push($this->verificationErrors, $e->toString());
}
}
}

View File

@ -0,0 +1,143 @@
<?php
class TemplateTest extends UiTestCase
{
public function testOpenTemplates()
{
$this->signInToBackend();
$this->open('cms');
$this->waitForPageToLoad("30000");
// Fix the sidebar
$this->click("xpath=(//a[@class='fix-button'])[1]");
/*
* Page
*/
// Create a new page
$this->click("xpath=(//form[@data-template-type='page']//button[@data-control='create-template'])[1]");
$this->waitForElementPresent("name=settings[title]");
// Populate page details
$this->type('name=settings[title]', 'Functional Test Page');
$this->type('name=settings[url]', '/xxx/functional/test/page');
$this->type('name=fileName', 'xxx_functional_test_page');
// Save the new page
$this->click("xpath=(//a[@data-request='onSave'])[1]");
$this->waitForElementPresent("xpath=(//li[@data-tab-id='page-".TEST_SELENIUM_THEME."-xxx_functional_test_page.htm'])[1]");
// Close the tab
$this->click("xpath=(//li[@data-tab-id='page-".TEST_SELENIUM_THEME."-xxx_functional_test_page.htm']/span[@class='tab-close'])[1]");
// Reopen the tab
$this->waitForElementPresent("xpath=(//div[@id='TemplateList-pageList-template-list']//li[@data-item-path='xxx_functional_test_page.htm']/a)[1]");
$this->click("xpath=(//div[@id='TemplateList-pageList-template-list']//li[@data-item-path='xxx_functional_test_page.htm']/a)[1]");
$this->waitForElementPresent("name=settings[title]");
sleep(1);
// Delete the page
$this->click("xpath=(//button[@data-request='onDelete'])[1]");
$this->getSweetConfirmation('Do you really want delete this page?');
// $this->assertTrue((bool)preg_match('/^Do you really want delete this page[\s\S]$/',$this->getConfirmation()));
$this->waitForElementNotPresent("name=settings[title]");
/*
* Partial
*/
// Click partials menu item
$this->click("xpath=(//li[@data-menu-item='partials']/a)[1]");
// Create a new partial
$this->click("xpath=(//form[@data-template-type='partial']//button[@data-control='create-template'])[1]");
$this->waitForElementPresent("name=fileName");
// Populate partial details
$this->type('name=fileName', 'xxx_functional_test_partial');
$this->type('name=settings[description]', 'Test partial');
// Save the new partial
$this->click("xpath=(//a[@data-request='onSave'])[1]");
$this->waitForElementPresent("xpath=(//li[@data-tab-id='partial-".TEST_SELENIUM_THEME."-xxx_functional_test_partial.htm'])[1]");
// Close the tab
$this->click("xpath=(//li[@data-tab-id='partial-".TEST_SELENIUM_THEME."-xxx_functional_test_partial.htm']/span[@class='tab-close'])[1]");
// Reopen the tab
$this->waitForElementPresent("xpath=(//div[@id='TemplateList-partialList-template-list']//li[@data-item-path='xxx_functional_test_partial.htm']/a)[1]");
$this->click("xpath=(//div[@id='TemplateList-partialList-template-list']//li[@data-item-path='xxx_functional_test_partial.htm']/a)[1]");
$this->waitForElementPresent("name=fileName");
sleep(1);
// Delete the partial
$this->click("xpath=(//button[@data-request='onDelete'])[1]");
$this->getSweetConfirmation('Do you really want delete this partial?');
$this->waitForElementNotPresent("name=fileName");
/*
* Layout
*/
// Click layouts menu item
$this->click("xpath=(//li[@data-menu-item='layouts']/a)[1]");
// Create a new layout
$this->click("xpath=(//form[@data-template-type='layout']//button[@data-control='create-template'])[1]");
$this->waitForElementPresent("name=fileName");
// Populate layout details
$this->type('name=fileName', 'xxx_functional_test_layout');
$this->type('name=settings[description]', 'Test layout');
// Save the new layout
$this->click("xpath=(//a[@data-request='onSave'])[1]");
$this->waitForElementPresent("xpath=(//li[@data-tab-id='layout-".TEST_SELENIUM_THEME."-xxx_functional_test_layout.htm'])[1]");
// Close the tab
$this->click("xpath=(//li[@data-tab-id='layout-".TEST_SELENIUM_THEME."-xxx_functional_test_layout.htm']/span[@class='tab-close'])[1]");
// Reopen the tab
$this->waitForElementPresent("xpath=(//div[@id='TemplateList-layoutList-template-list']//li[@data-item-path='xxx_functional_test_layout.htm']/a)[1]");
$this->click("xpath=(//div[@id='TemplateList-layoutList-template-list']//li[@data-item-path='xxx_functional_test_layout.htm']/a)[1]");
$this->waitForElementPresent("name=fileName");
sleep(1);
// Delete the layout
$this->click("xpath=(//button[@data-request='onDelete'])[1]");
$this->getSweetConfirmation('Do you really want delete this layout?');
$this->waitForElementNotPresent("name=fileName");
/*
* Content
*/
// Click contents menu item
$this->click("xpath=(//li[@data-menu-item='content']/a)[1]");
// Create a new content
$this->click("xpath=(//form[@data-template-type='content']//button[@data-control='create-template'])[1]");
$this->waitForElementPresent("name=fileName");
// Populate content details
$this->type('name=fileName', 'xxx_functional_test_content.txt');
// Save the new content
$this->click("xpath=(//a[@data-request='onSave'])[1]");
$this->waitForElementPresent("xpath=(//li[@data-tab-id='content-".TEST_SELENIUM_THEME."-xxx_functional_test_content.txt'])[1]");
// Close the tab
$this->click("xpath=(//li[@data-tab-id='content-".TEST_SELENIUM_THEME."-xxx_functional_test_content.txt']/span[@class='tab-close'])[1]");
// Reopen the tab
$this->waitForElementPresent("xpath=(//div[@id='TemplateList-contentList-template-list']//li[@data-item-path='xxx_functional_test_content.txt']/a)[1]");
$this->click("xpath=(//div[@id='TemplateList-contentList-template-list']//li[@data-item-path='xxx_functional_test_content.txt']/a)[1]");
$this->waitForElementPresent("name=fileName");
sleep(1);
// Delete the content
$this->click("xpath=(//button[@data-request='onDelete'])[1]");
$this->getSweetConfirmation('Do you really want delete this content file?');
$this->waitForElementNotPresent("name=fileName");
}
}

View File

@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<phpunit backupGlobals="false"
backupStaticAttributes="false"
bootstrap="../../bootstrap/autoload.php"
colors="true"
convertErrorsToExceptions="true"
convertNoticesToExceptions="true"
convertWarningsToExceptions="true"
processIsolation="false"
stopOnFailure="true"
syntaxCheck="false"
>
<testsuites>
<testsuite name="October Test Suite">
<directory>./</directory>
</testsuite>
</testsuites>
</phpunit>

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,7 @@ use October\Rain\Exception\SystemException;
class AuthManagerTest extends TestCase
{
public function setUp(): void
public function setUp()
{
$this->createApplication();
@ -23,7 +23,7 @@ class AuthManagerTest extends TestCase
]);
}
public function tearDown(): void
public function tearDown()
{
AuthManager::forgetInstance();
}

View File

@ -59,18 +59,18 @@ class NavigationManagerTest extends TestCase
$manager->setContext('October.Tester', 'blog');
$items = $manager->listSideMenuItems();
$this->assertIsArray($items);
$this->assertInternalType('array', $items);
$this->assertArrayHasKey('posts', $items);
$this->assertArrayHasKey('categories', $items);
$this->assertIsObject($items['posts']);
$this->assertInternalType('object', $items['posts']);
$this->assertObjectHasAttribute('code', $items['posts']);
$this->assertObjectHasAttribute('owner', $items['posts']);
$this->assertEquals('posts', $items['posts']->code);
$this->assertEquals('October.Tester', $items['posts']->owner);
$this->assertObjectHasAttribute('permissions', $items['posts']);
$this->assertIsArray($items['posts']->permissions);
$this->assertInternalType('array', $items['posts']->permissions);
$this->assertCount(1, $items['posts']->permissions);
$this->assertObjectHasAttribute('order', $items['posts']);
@ -92,7 +92,7 @@ class NavigationManagerTest extends TestCase
$items = $manager->listMainMenuItems();
$this->assertIsArray($items);
$this->assertInternalType('array', $items);
$this->assertArrayHasKey('OCTOBER.TESTER.PRINT', $items);
$item = $items['OCTOBER.TESTER.PRINT'];
@ -143,10 +143,10 @@ class NavigationManagerTest extends TestCase
$manager->setContext('October.Tester', 'blog');
$items = $manager->listSideMenuItems();
$this->assertIsArray($items);
$this->assertInternalType('array', $items);
$this->assertArrayHasKey('foo', $items);
$this->assertIsObject($items['foo']);
$this->assertInternalType('object', $items['foo']);
$this->assertObjectHasAttribute('code', $items['foo']);
$this->assertObjectHasAttribute('owner', $items['foo']);
$this->assertObjectHasAttribute('order', $items['foo']);
@ -156,7 +156,7 @@ class NavigationManagerTest extends TestCase
$this->assertEquals('October.Tester', $items['foo']->owner);
$this->assertObjectHasAttribute('permissions', $items['foo']);
$this->assertIsArray($items['foo']->permissions);
$this->assertInternalType('array', $items['foo']->permissions);
$this->assertCount(2, $items['foo']->permissions);
$this->assertContains('october.tester.access_foo', $items['foo']->permissions);
$this->assertContains('october.tester.access_bar', $items['foo']->permissions);

View File

@ -11,8 +11,8 @@ class BackendHelperTest extends TestCase
$assets = $backendHelper->decompileAsset('tests/fixtures/backend/assets/compilation.js');
$this->assertCount(2, $assets);
$this->assertStringContainsString('file1.js', $assets[0]);
$this->assertStringContainsString('file2.js', $assets[1]);
$this->assertContains('file1.js', $assets[0]);
$this->assertContains('file2.js', $assets[1]);
}
public function testDecompileMissingFile()

View File

@ -28,7 +28,7 @@ class WidgetMakerTest extends TestCase
*
* @return void
*/
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -1,119 +0,0 @@
<?php
use Cms\Classes\Theme;
use Cms\Classes\Asset;
class AssetTest extends TestCase
{
public function testLoad()
{
$theme = Theme::load('test');
// Valid direct path
$this->assertStringContainsString(
'console.log(\'script1.js\');',
Asset::load($theme, 'js/script1.js')->content
);
// Valid direct subdirectory path
$this->assertStringContainsString(
'console.log(\'subdir/script1.js\');',
Asset::load($theme, 'js/subdir/script1.js')->content
);
// Valid relative path
$this->assertStringContainsString(
'console.log(\'script2.js\');',
Asset::load($theme, 'js/subdir/../script2.js')->content
);
// Invalid theme path
$this->assertNull(
Asset::load($theme, 'js/invalid.js')
);
// Check that we cannot break out of assets directory
$this->assertNull(
Asset::load($theme, '../../../../js/helpers/fakeDom.js')
);
$this->assertNull(
Asset::load($theme, '../content/html-content.htm')
);
// Check that we cannot load directories directly
$this->assertNull(
Asset::load($theme, 'js/subdir')
);
// Check that we definitely cannot load external PHP files
$this->assertNull(
Asset::load($theme, '../../../../../config/database.php')
);
}
public function testGetPath()
{
// Test some pathing fringe cases
$theme = Theme::load('test');
$assetClass = new Asset($theme);
$themeDir = $theme->getPath();
// Direct paths
$this->assertEquals(
$themeDir . '/assets/js/script1.js',
$assetClass->getFilePath('js/script1.js')
);
$this->assertEquals(
$themeDir . '/assets/js/script1.js',
$assetClass->getFilePath('/js/script1.js')
);
// Direct path to a directory
$this->assertEquals(
$themeDir . '/assets/js/subdir',
$assetClass->getFilePath('/js/subdir')
);
$this->assertEquals(
$themeDir . '/assets/js/subdir',
$assetClass->getFilePath('/js/subdir/')
);
// Relative paths
$this->assertEquals(
$themeDir . '/assets/js/script2.js',
$assetClass->getFilePath('./js/script2.js')
);
$this->assertEquals(
$themeDir . '/assets/js/script2.js',
$assetClass->getFilePath('/js/subdir/../script2.js')
);
// Missing file, but valid directory (allows for new files)
$this->assertEquals(
$themeDir . '/assets/js/missing.js',
$assetClass->getFilePath('/js/missing.js')
);
$this->assertEquals(
$themeDir . '/assets/js/missing.js',
$assetClass->getFilePath('js/missing.js')
);
// Missing file and missing directory (new directories are created as needed)
$this->assertEquals(
$themeDir . '/assets/js/missing/missing.js',
$assetClass->getFilePath('/js/missing/missing.js')
);
// Ensure we cannot get paths outside of the assets directory
$this->assertFalse(
$assetClass->getFilePath('../../../../js/helpers/fakeDom.js')
);
$this->assertFalse(
$assetClass->getFilePath('../content/html-content.htm')
);
$this->assertFalse(
$assetClass->getFilePath('../../../../../config/database.php')
);
}
}

View File

@ -30,7 +30,7 @@ class TestTemporaryCmsCompoundObject extends CmsCompoundObject
class CmsCompoundObjectTest extends TestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();
Model::clearBootedModels();
@ -44,16 +44,16 @@ class CmsCompoundObjectTest extends TestCase
$theme = Theme::load('test');
$obj = TestCmsCompoundObject::load($theme, 'compound.htm');
$this->assertStringContainsString("\$controller->data['something'] = 'some value'", $obj->code);
$this->assertContains("\$controller->data['something'] = 'some value'", $obj->code);
$this->assertEquals('<p>This is a paragraph</p>', $obj->markup);
$this->assertIsArray($obj->settings);
$this->assertInternalType('array', $obj->settings);
$this->assertArrayHasKey('var', $obj->settings);
$this->assertEquals('value', $obj->settings['var']);
$this->assertArrayHasKey('components', $obj->settings);
$this->assertArrayHasKey('section', $obj->settings['components']);
$this->assertIsArray($obj->settings['components']['section']);
$this->assertInternalType('array', $obj->settings['components']['section']);
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
$this->assertEquals(10, $obj->settings['components']['section']['version']);
@ -69,7 +69,7 @@ class CmsCompoundObjectTest extends TestCase
$obj = TestCmsCompoundObject::load($theme, 'component.htm');
$this->assertArrayHasKey('components', $obj->settings);
$this->assertIsArray($obj->settings['components']);
$this->assertInternalType('array', $obj->settings['components']);
$this->assertArrayHasKey('testArchive', $obj->settings['components']);
$this->assertArrayHasKey('posts-per-page', $obj->settings['components']['testArchive']);
$this->assertEquals(10, $obj->settings['components']['testArchive']['posts-per-page']);
@ -82,7 +82,7 @@ class CmsCompoundObjectTest extends TestCase
$obj = TestCmsCompoundObject::load($theme, 'components.htm');
$this->assertArrayHasKey('components', $obj->settings);
$this->assertIsArray($obj->settings['components']);
$this->assertInternalType('array', $obj->settings['components']);
$this->assertArrayHasKey('testArchive firstAlias', $obj->settings['components']);
$this->assertArrayHasKey('October\Tester\Components\Post secondAlias', $obj->settings['components']);
@ -108,7 +108,7 @@ class CmsCompoundObjectTest extends TestCase
$properties = $obj->getComponentProperties('October\Tester\Components\Post');
$emptyProperties = $obj->getComponentProperties('October\Tester\Components\Archive');
$notExistingProperties = $obj->getComponentProperties('This\Is\Not\Component');
$this->assertIsArray($properties);
$this->assertInternalType('array', $properties);
$this->assertArrayHasKey('show-featured', $properties);
$this->assertTrue((bool)$properties['show-featured']);
$this->assertEquals('true', $properties['show-featured']);
@ -148,18 +148,18 @@ class CmsCompoundObjectTest extends TestCase
$this->assertEquals($testContent, $obj->getContent());
$this->assertEquals('testcompound.htm', $obj->getFileName());
$this->assertEquals('<p>This is a paragraph</p>', $obj->markup);
$this->assertIsArray($obj->settings);
$this->assertInternalType('array', $obj->settings);
$this->assertArrayHasKey('var', $obj->settings);
$this->assertEquals('value', $obj->settings['var']);
$this->assertArrayHasKey('components', $obj->settings);
$this->assertIsArray($obj->settings['components']['section']);
$this->assertInternalType('array', $obj->settings['components']['section']);
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
$this->assertEquals(10, $obj->settings['components']['section']['version']);
$this->assertEquals('value', $obj->var);
$this->assertIsArray($obj->settings['components']['section']);
$this->assertInternalType('array', $obj->settings['components']['section']);
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
$this->assertEquals(10, $obj->settings['components']['section']['version']);
@ -173,18 +173,18 @@ class CmsCompoundObjectTest extends TestCase
$this->assertEquals($testContent, $obj->getContent());
$this->assertEquals('testcompound.htm', $obj->getFileName());
$this->assertEquals('<p>This is a paragraph</p>', $obj->markup);
$this->assertIsArray($obj->settings);
$this->assertInternalType('array', $obj->settings);
$this->assertArrayHasKey('var', $obj->settings);
$this->assertEquals('value', $obj->settings['var']);
$this->assertArrayHasKey('components', $obj->settings);
$this->assertIsArray($obj->settings['components']['section']);
$this->assertInternalType('array', $obj->settings['components']['section']);
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
$this->assertEquals(10, $obj->settings['components']['section']['version']);
$this->assertEquals('value', $obj->var);
$this->assertIsArray($obj->settings['components']['section']);
$this->assertInternalType('array', $obj->settings['components']['section']);
$this->assertArrayHasKey('version', $obj->settings['components']['section']);
$this->assertEquals(10, $obj->settings['components']['section']['version']);
}
@ -280,14 +280,14 @@ class CmsCompoundObjectTest extends TestCase
$obj = TestParsedCmsCompoundObject::load($theme, 'viewbag.htm');
$this->assertNull($obj->code);
$this->assertEquals('<p>Chop Suey!</p>', $obj->markup);
$this->assertIsArray($obj->settings);
$this->assertInternalType('array', $obj->settings);
$this->assertArrayHasKey('var', $obj->settings);
$this->assertEquals('value', $obj->settings['var']);
$this->assertArrayHasKey('components', $obj->settings);
$this->assertArrayHasKey('viewBag', $obj->settings['components']);
$this->assertIsArray($obj->settings['components']['viewBag']);
$this->assertInternalType('array', $obj->settings['components']['viewBag']);
$this->assertArrayHasKey('title', $obj->settings['components']['viewBag']);
$this->assertEquals('Toxicity', $obj->settings['components']['viewBag']['title']);

View File

@ -7,7 +7,7 @@ use October\Rain\Halcyon\Model;
class CmsObjectQueryTest extends TestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -148,11 +148,12 @@ class CmsObjectTest extends TestCase
$this->assertNull($obj->something);
}
/**
* @expectedException \October\Rain\Exception\ValidationException
* @expectedExceptionMessage Invalid file name
*/
public function testFillInvalidFileNameSymbol()
{
$this->expectException(\October\Rain\Exception\ValidationException::class);
$this->expectExceptionMessage('Invalid file name');
$theme = Theme::load('apitest');
$testContents = 'mytestcontent';
@ -163,11 +164,12 @@ class CmsObjectTest extends TestCase
$obj->save();
}
/**
* @expectedException \October\Rain\Exception\ValidationException
* @expectedExceptionMessage Invalid file name
*/
public function testFillInvalidFileNamePath()
{
$this->expectException(\October\Rain\Exception\ValidationException::class);
$this->expectExceptionMessage('Invalid file name');
$theme = Theme::load('apitest');
$testContents = 'mytestcontent';
@ -178,11 +180,12 @@ class CmsObjectTest extends TestCase
$obj->save();
}
/**
* @expectedException \October\Rain\Exception\ValidationException
* @expectedExceptionMessage Invalid file name
*/
public function testFillInvalidFileSlash()
{
$this->expectException(\October\Rain\Exception\ValidationException::class);
$this->expectExceptionMessage('Invalid file name');
$theme = Theme::load('apitest');
$testContents = 'mytestcontent';
@ -193,11 +196,12 @@ class CmsObjectTest extends TestCase
$obj->save();
}
/**
* @expectedException \October\Rain\Exception\ValidationException
* @expectedExceptionMessage The File Name field is required
*/
public function testFillEmptyFileName()
{
$this->expectException(\October\Rain\Exception\ValidationException::class);
$this->expectExceptionMessage('The File Name field is required');
$theme = Theme::load('apitest');
$testContents = 'mytestcontent';
@ -262,12 +266,11 @@ class CmsObjectTest extends TestCase
/**
* @depends testRename
* @expectedException \October\Rain\Exception\ApplicationException
* @expectedExceptionMessage already exists
*/
public function testRenameToExistingFile()
{
$this->expectException(\October\Rain\Exception\ApplicationException::class);
$this->expectExceptionMessageMatches('/already\sexists/');
$theme = Theme::load('apitest');
$srcFilePath = $theme->getPath().'/testobjects/anotherobj.htm';

View File

@ -10,7 +10,7 @@ use Cms\Classes\Controller;
class CodeParserTest extends TestCase
{
public function setUp() : void
public function setUp()
{
parent::setup();
@ -41,7 +41,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($layout);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);
@ -78,7 +78,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($layout);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertEquals('request-cache', $info['source']);
$this->assertFileExists($info['filePath']);
@ -91,7 +91,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($layout);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertEquals('cache', $info['source']);
$this->assertFileExists($info['filePath']);
@ -101,7 +101,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($layout);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertEquals('request-cache', $info['source']);
$this->assertFileExists($info['filePath']);
@ -110,14 +110,13 @@ class CodeParserTest extends TestCase
*/
$this->assertTrue(@touch($layout->getFilePath()));
clearstatcache();
$layout = Layout::load($theme, 'php-parser-test.htm');
$this->assertNotEmpty($layout);
$parser = new CodeParser($layout);
$property->setValue($parser, []);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertEquals('parser', $info['source']);
$this->assertFileExists($info['filePath']);
}
@ -132,7 +131,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($layout);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);
@ -158,7 +157,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($page);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);
@ -192,7 +191,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($page);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);
@ -221,7 +220,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($page);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);
@ -256,7 +255,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($page);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);
@ -285,7 +284,7 @@ class CodeParserTest extends TestCase
$parser = new CodeParser($page);
$info = $parser->parse();
$this->assertIsArray($info);
$this->assertInternalType('array', $info);
$this->assertArrayHasKey('filePath', $info);
$this->assertArrayHasKey('className', $info);
$this->assertArrayHasKey('source', $info);

View File

@ -9,7 +9,7 @@ use Cms\Classes\ComponentManager;
class ComponentManagerTest extends TestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -6,7 +6,7 @@ use October\Rain\Halcyon\Model;
class ControllerTest extends TestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();
@ -69,7 +69,7 @@ class ControllerTest extends TestCase
$response = $controller->run('/some-page-that-doesnt-exist');
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$content = $response->getContent();
$this->assertIsString($content);
$this->assertInternalType('string', $content);
$this->assertEquals('<p>Page not found</p>', $content);
}
@ -83,15 +83,16 @@ class ControllerTest extends TestCase
$response = $controller->run('/');
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$content = $response->getContent();
$this->assertIsString($content);
$this->assertInternalType('string', $content);
$this->assertEquals('<h1>My Webpage</h1>', trim($content));
}
/**
* @expectedException Cms\Classes\CmsException
* @expectedExceptionMessage is not found
*/
public function testLayoutNotFound()
{
$this->expectException(\Cms\Classes\CmsException::class);
$this->expectExceptionMessageMatches('/is\snot\sfound/');
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/no-layout');
@ -145,11 +146,12 @@ class ControllerTest extends TestCase
$this->assertEquals("<div>LAYOUT CONTENT <h1>This page is a subdirectory</h1></div>", $response);
}
/**
* @expectedException \Twig\Error\RuntimeError
* @expectedExceptionMessage is not found
*/
public function testPartialNotFound()
{
$this->expectException(\Twig\Error\RuntimeError::class);
$this->expectExceptionMessageMatches('/is\snot\sfound/');
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/no-partial')->getContent();
@ -191,11 +193,12 @@ class ControllerTest extends TestCase
return $requestMock;
}
/**
* @expectedException Cms\Classes\CmsException
* @expectedExceptionMessage AJAX handler 'onNoHandler' was not found.
*/
public function testAjaxHandlerNotFound()
{
$this->expectException(\Cms\Classes\CmsException::class);
$this->expectExceptionMessage('AJAX handler \'onNoHandler\' was not found.');
Request::swap($this->configAjaxRequestMock('onNoHandler', ''));
$theme = Theme::load('test');
@ -203,11 +206,12 @@ class ControllerTest extends TestCase
$controller->run('/ajax-test');
}
/**
* @expectedException Cms\Classes\CmsException
* @expectedExceptionMessage Invalid AJAX handler name: delete.
*/
public function testAjaxInvalidHandlerName()
{
$this->expectException(\Cms\Classes\CmsException::class);
$this->expectExceptionMessage('Invalid AJAX handler name: delete.');
Request::swap($this->configAjaxRequestMock('delete'));
$theme = Theme::load('test');
@ -215,11 +219,12 @@ class ControllerTest extends TestCase
$controller->run('/ajax-test');
}
/**
* @expectedException Cms\Classes\CmsException
* @expectedExceptionMessage Invalid partial name: p:artial.
*/
public function testAjaxInvalidPartial()
{
$this->expectException(\Cms\Classes\CmsException::class);
$this->expectExceptionMessage('Invalid partial name: p:artial.');
Request::swap($this->configAjaxRequestMock('onTest', 'p:artial'));
$theme = Theme::load('test');
@ -227,11 +232,12 @@ class ControllerTest extends TestCase
$controller->run('/ajax-test');
}
/**
* @expectedException Cms\Classes\CmsException
* @expectedExceptionMessage The partial 'partial' is not found.
*/
public function testAjaxPartialNotFound()
{
$this->expectException(\Cms\Classes\CmsException::class);
$this->expectExceptionMessage('The partial \'partial\' is not found.');
Request::swap($this->configAjaxRequestMock('onTest', 'partial'));
$theme = Theme::load('test');
@ -249,7 +255,7 @@ class ControllerTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$content = $response->getOriginalContent();
$this->assertIsArray($content);
$this->assertInternalType('array', $content);
$this->assertEquals(200, $response->getStatusCode());
$this->assertCount(1, $content);
$this->assertArrayHasKey('ajax-result', $content);
@ -266,7 +272,7 @@ class ControllerTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$content = $response->getOriginalContent();
$this->assertIsArray($content);
$this->assertInternalType('array', $content);
$this->assertEquals(200, $response->getStatusCode());
$this->assertCount(1, $content);
$this->assertArrayHasKey('ajax-result', $content);
@ -283,7 +289,7 @@ class ControllerTest extends TestCase
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$content = $response->getOriginalContent();
$this->assertIsArray($content);
$this->assertInternalType('array', $content);
$this->assertEquals(200, $response->getStatusCode());
$this->assertCount(2, $content);
$this->assertArrayHasKey('ajax-result', $content);
@ -297,7 +303,7 @@ class ControllerTest extends TestCase
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/with-component')->getContent();
$page = self::getProtectedProperty($controller, 'page');
$page = $this->readAttribute($controller, 'page');
$this->assertArrayHasKey('testArchive', $page->components);
$component = $page->components['testArchive'];
@ -325,7 +331,7 @@ ESC;
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/with-components')->getContent();
$page = self::getProtectedProperty($controller, 'page');
$page = $this->readAttribute($controller, 'page');
$this->assertArrayHasKey('firstAlias', $page->components);
$this->assertArrayHasKey('secondAlias', $page->components);
@ -357,18 +363,19 @@ ESC;
$this->assertInstanceOf('Symfony\Component\HttpFoundation\Response', $response);
$content = $response->getOriginalContent();
$this->assertIsArray($content);
$this->assertInternalType('array', $content);
$this->assertEquals(200, $response->getStatusCode());
$this->assertCount(1, $content);
$this->assertArrayHasKey('ajax-result', $content);
$this->assertEquals('page', $content['ajax-result']);
}
/**
* @expectedException October\Rain\Exception\SystemException
* @expectedExceptionMessage is not registered for the component
*/
public function testComponentClassNotFound()
{
$this->expectException(\October\Rain\Exception\SystemException::class);
$this->expectExceptionMessageMatches('/is\snot\sregistered\sfor\sthe\scomponent/');
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/no-component-class')->getContent();
@ -388,7 +395,7 @@ ESC;
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/with-soft-component-class')->getContent();
$page = $controller->getPage();
$page = $this->readAttribute($controller, 'page');
$this->assertArrayHasKey('testArchive', $page->components);
$component = $page->components['testArchive'];
@ -414,7 +421,7 @@ ESC;
$theme = Theme::load('test');
$controller = new Controller($theme);
$response = $controller->run('/with-soft-component-class-alias')->getContent();
$page = $controller->getPage();
$page = $this->readAttribute($controller, 'page');
$this->assertArrayHasKey('someAlias', $page->components);
$component = $page->components['someAlias'];

View File

@ -7,7 +7,7 @@ class RouterTest extends TestCase
{
protected static $theme = null;
public function setUp() : void
public function setUp()
{
parent::setUp();
@ -43,7 +43,7 @@ class RouterTest extends TestCase
$this->assertFalse($value);
$map = $property->getValue($router);
$this->assertIsArray($map);
$this->assertInternalType('array', $map);
$this->assertGreaterThanOrEqual(4, count($map));
/*
@ -52,7 +52,7 @@ class RouterTest extends TestCase
$value = $method->invoke($router);
$this->assertTrue($value);
$map = $property->getValue($router);
$this->assertIsArray($map);
$this->assertInternalType('array', $map);
$this->assertGreaterThanOrEqual(4, count($map));
}

View File

@ -4,7 +4,7 @@ use Cms\Classes\Theme;
class ThemeTest extends TestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();
@ -44,7 +44,7 @@ class ThemeTest extends TestCase
$pageCollection = $theme->listPages();
$pages = array_values($pageCollection->all());
$this->assertIsArray($pages);
$this->assertInternalType('array', $pages);
$expectedPageNum = $this->countThemePages(base_path().'/tests/fixtures/themes/test/pages');
$this->assertCount($expectedPageNum, $pages);
@ -63,11 +63,12 @@ class ThemeTest extends TestCase
$this->assertEquals('test', $activeTheme->getDirName());
}
/**
* @expectedException \October\Rain\Exception\SystemException
* @expectedExceptionMessage The active theme is not set.
*/
public function testNoActiveTheme()
{
$this->expectException(\October\Rain\Exception\SystemException::class);
$this->expectExceptionMessage('The active theme is not set.');
Config::set('cms.activeTheme', null);
Theme::getActiveTheme();
}

View File

@ -5,7 +5,7 @@ use Database\Tester\Models\User;
class AttachManyModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -7,7 +7,7 @@ use Symfony\Component\HttpFoundation\File\UploadedFile;
class AttachOneModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -5,7 +5,7 @@ use Database\Tester\Models\Author;
class BelongsToManyModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -5,7 +5,7 @@ use Database\Tester\Models\Author;
class BelongsToModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -6,7 +6,7 @@ use October\Rain\Database\Models\DeferredBinding;
class DeferredBindingTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -6,7 +6,7 @@ use October\Rain\Database\Collection;
class HasManyModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();
@ -63,6 +63,8 @@ class HasManyModelTest extends PluginTestCase
public function testGetRelationValue()
{
$this->markTestSkipped('Marked as \'skipped\' for further investigation');
Model::unguard();
$author = Author::create(['name' => 'Stevie']);
$post1 = Post::create(['title' => "First post", 'author_id' => $author->id]);

View File

@ -1,54 +0,0 @@
<?php
use Database\Tester\Models\Author;
use Database\Tester\Models\Country;
use Database\Tester\Models\Post;
use October\Rain\Database\Collection;
class HasManyThroughModelTest extends PluginTestCase
{
public function setUp() : void
{
parent::setUp();
include_once base_path().'/tests/fixtures/plugins/database/tester/models/Post.php';
include_once base_path().'/tests/fixtures/plugins/database/tester/models/Author.php';
include_once base_path().'/tests/fixtures/plugins/database/tester/models/Country.php';
$this->runPluginRefreshCommand('Database.Tester');
}
public function testGet()
{
Model::unguard();
$country = Country::create(['name' => 'Australia']);
$author1 = Author::create(['name' => 'Stevie', 'email' => 'stevie@email.tld']);
$author2 = Author::create(['name' => 'Louie', 'email' => 'louie@email.tld']);
$post1 = Post::create(['title' => "First post", 'description' => "Yay!!"]);
$post2 = Post::create(['title' => "Second post", 'description' => "Woohoo!!"]);
$post3 = Post::create(['title' => "Third post", 'description' => "Yipiee!!"]);
$post4 = Post::make(['title' => "Fourth post", 'description' => "Hooray!!"]);
Model::reguard();
// Set data
$author1->country = $country;
$author2->country = $country;
$author1->posts = new Collection([$post1, $post2]);
$author2->posts = new Collection([$post3, $post4]);
$author1->save();
$author2->save();
$country = Country::with([
'posts'
])->find($country->id);
$this->assertEquals([
$post1->id,
$post2->id,
$post3->id,
$post4->id
], $country->posts->pluck('id')->toArray());
}
}

View File

@ -5,7 +5,7 @@ use Database\Tester\Models\Phone;
class HasOneModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

View File

@ -1,39 +0,0 @@
<?php
use Database\Tester\Models\Author;
use Database\Tester\Models\Phone;
use Database\Tester\Models\User;
class HasOneThroughModelTest extends PluginTestCase
{
public function setUp() : void
{
parent::setUp();
include_once base_path().'/tests/fixtures/plugins/database/tester/models/User.php';
include_once base_path().'/tests/fixtures/plugins/database/tester/models/Author.php';
include_once base_path().'/tests/fixtures/plugins/database/tester/models/Phone.php';
$this->runPluginRefreshCommand('Database.Tester');
}
public function testGet()
{
Model::unguard();
$phone = Phone::create(['number' => '08 1234 5678']);
$author = Author::create(['name' => 'Stevie', 'email' => 'stevie@email.tld']);
$user = User::create(['name' => 'Stevie', 'email' => 'stevie@email.tld']);
Model::reguard();
// Set data
$author->phone = $phone;
$author->user = $user;
$author->save();
$user = User::with([
'phone'
])->find($user->id);
$this->assertEquals($phone->id, $user->phone->id);
}
}

View File

@ -4,7 +4,7 @@ use Database\Tester\Models\Post;
class ModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();
@ -23,11 +23,12 @@ class ModelTest extends PluginTestCase
$this->assertEquals(1, $post->id);
}
/**
* @expectedException \Illuminate\Database\Eloquent\MassAssignmentException
* @expectedExceptionMessage title
*/
public function testGuardedAttribute()
{
$this->expectException(\Illuminate\Database\Eloquent\MassAssignmentException::class);
$this->expectExceptionMessageMatches('/title/');
Post::create(['title' => 'Hi!', 'slug' => 'authenticity']);
}
}

View File

@ -6,7 +6,7 @@ use October\Rain\Database\Collection;
class MorphManyModelTest extends PluginTestCase
{
public function setUp() : void
public function setUp()
{
parent::setUp();

Some files were not shown because too many files have changed in this diff Show More