Improvements in the table widget, added new events to the CMS core, form styling, added support for the Table widget in in the CMS area.
This commit is contained in:
commit
c26545913c
|
|
@ -1,4 +1,7 @@
|
|||
* **Build 17x** (2014-12-xx)
|
||||
* **Build 17x** (2015-01-xx)
|
||||
- The variable `errors` will be included in a CMS page when redirecting via `Redirect::withErrors($validator)`.
|
||||
|
||||
* **Build 174** (2015-01-05)
|
||||
- Improved asset caching (`cms.enableAssetCache`), when enabled the server will send a *304 Not Modified* header.
|
||||
- Introduced new *Table* widget and *DataTable* form widget.
|
||||
- There is now a simpler way for sending mail via `Mail::sendTo()`.
|
||||
|
|
|
|||
|
|
@ -96,4 +96,16 @@ return array(
|
|||
*/
|
||||
|
||||
'twigNoCache' => true,
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Convert Line Endings
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Determines if October should convert line endings from the windows style
|
||||
| \r\n to the unix style \n.
|
||||
|
|
||||
*/
|
||||
|
||||
'convertLineEndings' => true,
|
||||
);
|
||||
|
|
|
|||
|
|
@ -9013,6 +9013,7 @@ label {
|
|||
}
|
||||
.help-block {
|
||||
font-size: 12px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.help-block.before-field {
|
||||
margin-top: 0;
|
||||
|
|
|
|||
|
|
@ -250,8 +250,7 @@
|
|||
if ($('> li > a', this.$tabsContainer).length == 0)
|
||||
this.$el.trigger('afterAllClosed.oc.tab')
|
||||
|
||||
|
||||
this.$el.trigger('closed.oc.tab')
|
||||
this.$el.trigger('closed.oc.tab', [$tab])
|
||||
|
||||
$(window).trigger('resize')
|
||||
this.updateClasses()
|
||||
|
|
|
|||
|
|
@ -187,6 +187,7 @@ label {
|
|||
|
||||
.help-block {
|
||||
font-size: 12px;
|
||||
margin-bottom: 0;
|
||||
&.before-field {
|
||||
margin-top: 0;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ use Backend\Classes\ControllerBehavior;
|
|||
*/
|
||||
class ListController extends ControllerBehavior
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array List definitions, keys for alias and value for configuration.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use October\Rain\Router\Helper as RouterHelper;
|
|||
*/
|
||||
class BackendHelper
|
||||
{
|
||||
|
||||
/**
|
||||
* Returns a URL in context of the Backend
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -395,4 +395,54 @@ class FormField
|
|||
|
||||
return Str::evalHtmlId($id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns this fields value from a supplied data set, which can be
|
||||
* an array or a model or another generic collection.
|
||||
* @param mixed $data
|
||||
* @return mixed
|
||||
*/
|
||||
public function getValueFromData($data, $default = null)
|
||||
{
|
||||
$fieldName = $this->fieldName;
|
||||
|
||||
/*
|
||||
* Array field name, eg: field[key][key2][key3]
|
||||
*/
|
||||
$keyParts = Str::evalHtmlArray($fieldName);
|
||||
$lastField = end($keyParts);
|
||||
$result = $data;
|
||||
|
||||
/*
|
||||
* Loop the field key parts and build a value.
|
||||
* To support relations only the last field should return the
|
||||
* relation value, all others will look up the relation object as normal.
|
||||
*/
|
||||
foreach ($keyParts as $key) {
|
||||
|
||||
if ($result instanceof Model && $result->hasRelation($key)) {
|
||||
if ($key == $lastField) {
|
||||
$result = $result->getRelationValue($key) ?: $default;
|
||||
}
|
||||
else {
|
||||
$result = $result->{$key};
|
||||
}
|
||||
}
|
||||
elseif (is_array($result)) {
|
||||
if (!array_key_exists($key, $result)) {
|
||||
return $default;
|
||||
}
|
||||
$result = $result[$key];
|
||||
}
|
||||
else {
|
||||
if (!isset($result->{$key})) {
|
||||
return $default;
|
||||
}
|
||||
$result = $result->{$key};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use ArrayAccess;
|
|||
*/
|
||||
class FormTabs implements IteratorAggregate, ArrayAccess
|
||||
{
|
||||
|
||||
const SECTION_OUTSIDE = 'outside';
|
||||
const SECTION_PRIMARY = 'primary';
|
||||
const SECTION_SECONDARY = 'secondary';
|
||||
|
|
@ -176,5 +175,4 @@ class FormTabs implements IteratorAggregate, ArrayAccess
|
|||
{
|
||||
return isset($this->fields[$offset]) ? $this->fields[$offset] : null;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use Str;
|
|||
*/
|
||||
abstract class FormWidgetBase extends WidgetBase
|
||||
{
|
||||
|
||||
/**
|
||||
* @var FormField Object containing general form field information.
|
||||
*/
|
||||
|
|
@ -88,11 +87,11 @@ abstract class FormWidgetBase extends WidgetBase
|
|||
}
|
||||
|
||||
/**
|
||||
* Process the postback data for this widget.
|
||||
* Process the postback value for this widget.
|
||||
* @param $value The existing value for this widget.
|
||||
* @return string The new value for this widget.
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
return $value;
|
||||
}
|
||||
|
|
@ -102,25 +101,19 @@ abstract class FormWidgetBase extends WidgetBase
|
|||
* supports nesting via HTML array.
|
||||
* @return string
|
||||
*/
|
||||
public function getLoadData()
|
||||
public function getLoadValue()
|
||||
{
|
||||
list($model, $attribute) = $this->getModelArrayAttribute($this->valueFrom);
|
||||
|
||||
if (!is_null($model)) {
|
||||
return $model->{$attribute};
|
||||
}
|
||||
|
||||
return null;
|
||||
return $this->formField->getValueFromData($this->model);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the final model and attribute name of
|
||||
* a nested HTML array attribute.
|
||||
* Eg: list($model, $attribute) = $this->getModelArrayAttribute($this->valueFrom);
|
||||
* Eg: list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
|
||||
* @param string $attribute.
|
||||
* @return array
|
||||
*/
|
||||
public function getModelArrayAttribute($attribute)
|
||||
public function resolveModelAttribute($attribute)
|
||||
{
|
||||
$model = $this->model;
|
||||
$parts = Str::evalHtmlArray($attribute);
|
||||
|
|
@ -132,4 +125,5 @@ abstract class FormWidgetBase extends WidgetBase
|
|||
|
||||
return [$model, $last];
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
class ListColumn
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string List column name.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use Session;
|
|||
*/
|
||||
abstract class WidgetBase
|
||||
{
|
||||
|
||||
use \System\Traits\AssetMaker;
|
||||
use \System\Traits\ConfigMaker;
|
||||
use \System\Traits\ViewMaker;
|
||||
|
|
|
|||
|
|
@ -2,12 +2,10 @@
|
|||
|
||||
use Str;
|
||||
use File;
|
||||
use Lang;
|
||||
use Closure;
|
||||
use October\Rain\Support\Yaml;
|
||||
use Illuminate\Container\Container;
|
||||
use System\Classes\PluginManager;
|
||||
use System\Classes\SystemException;
|
||||
|
||||
/**
|
||||
* Widget manager
|
||||
|
|
@ -57,34 +55,6 @@ class WidgetManager
|
|||
$this->pluginManager = PluginManager::instance();
|
||||
}
|
||||
|
||||
/**
|
||||
* Makes a widget object with configuration set.
|
||||
* @param string $className A widget class name.
|
||||
* @param Controller $controller The Backend controller that spawned this widget.
|
||||
* @param array $configuration Configuration values.
|
||||
* @return WidgetBase The widget object.
|
||||
*/
|
||||
public function makeWidget($className, $controller = null, $configuration = null)
|
||||
{
|
||||
/*
|
||||
* Build configuration
|
||||
*/
|
||||
if ($configuration === null) {
|
||||
$configuration = [];
|
||||
}
|
||||
|
||||
/*
|
||||
* Create widget object
|
||||
*/
|
||||
if (!class_exists($className)) {
|
||||
throw new SystemException(Lang::get('backend::lang.widget.not_registered', [
|
||||
'name' => $className
|
||||
]));
|
||||
}
|
||||
|
||||
return new $className($controller, $configuration);
|
||||
}
|
||||
|
||||
//
|
||||
// Form Widgets
|
||||
//
|
||||
|
|
|
|||
|
|
@ -21,7 +21,6 @@ use Exception;
|
|||
*/
|
||||
class AccessLogs extends Controller
|
||||
{
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.ListController'
|
||||
];
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ use Backend\Models\EditorPreferences as EditorPreferencesModel;
|
|||
*/
|
||||
class EditorPreferences extends Controller
|
||||
{
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -106,7 +106,7 @@ class CodeEditor extends FormWidgetBase
|
|||
$this->vars['stretch'] = $this->formField->stretch;
|
||||
$this->vars['size'] = $this->formField->size;
|
||||
$this->vars['name'] = $this->formField->getName();
|
||||
$this->vars['value'] = $this->getLoadData();
|
||||
$this->vars['value'] = $this->getLoadValue();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ class ColorPicker extends FormWidgetBase
|
|||
public function prepareVars()
|
||||
{
|
||||
$this->vars['name'] = $this->formField->getName();
|
||||
$this->vars['value'] = $value = $this->getLoadData();
|
||||
$this->vars['value'] = $value = $this->getLoadValue();
|
||||
$this->vars['availableColors'] = $this->availableColors;
|
||||
$this->vars['isCustomColor'] = !in_array($value, $this->availableColors);
|
||||
}
|
||||
|
|
@ -74,7 +74,7 @@ class ColorPicker extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
return strlen($value) ? $value : null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class DataGrid extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
return json_decode($value);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class DataTable extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
// TODO: provide a streaming implementation of saving
|
||||
// data to the model. The current implementation returns
|
||||
|
|
@ -96,7 +96,7 @@ class DataTable extends FormWidgetBase
|
|||
// data from the model. The current implementation loads
|
||||
// all records at once. -ab
|
||||
|
||||
$records = $this->getLoadData() ?: [];
|
||||
$records = $this->getLoadValue() ?: [];
|
||||
$dataSource->initRecords((array) $records);
|
||||
}
|
||||
|
||||
|
|
@ -104,7 +104,13 @@ class DataTable extends FormWidgetBase
|
|||
{
|
||||
$config = $this->makeConfig((array) $this->config);
|
||||
$config->dataSource = 'client';
|
||||
$config->alias = $this->alias . 'Table';
|
||||
|
||||
// It's safe to use the field name as an alias
|
||||
// as field names do not repeat in forms. This
|
||||
// approach lets to access the table data by the
|
||||
// field name in POST requests directly (required
|
||||
// in some edge cases).
|
||||
$config->alias = $this->fieldName;
|
||||
|
||||
$table = new Table($this->controller, $config);
|
||||
|
||||
|
|
|
|||
|
|
@ -62,7 +62,7 @@ class DatePicker extends FormWidgetBase
|
|||
$this->vars['timeName'] = self::TIME_PREFIX.$this->formField->getName(false);
|
||||
$this->vars['timeValue'] = null;
|
||||
|
||||
if ($value = $this->getLoadData()) {
|
||||
if ($value = $this->getLoadValue()) {
|
||||
|
||||
/*
|
||||
* Date / Time
|
||||
|
|
@ -120,7 +120,7 @@ class DatePicker extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
if (!strlen($value)) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -109,7 +109,7 @@ class FileUpload extends FormWidgetBase
|
|||
*/
|
||||
protected function getRelationObject()
|
||||
{
|
||||
list($model, $attribute) = $this->getModelArrayAttribute($this->valueFrom);
|
||||
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
|
||||
return $model->{$attribute}();
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ class FileUpload extends FormWidgetBase
|
|||
*/
|
||||
protected function getRelationType()
|
||||
{
|
||||
list($model, $attribute) = $this->getModelArrayAttribute($this->valueFrom);
|
||||
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
|
||||
return $model->getRelationType($attribute);
|
||||
}
|
||||
|
||||
|
|
@ -195,7 +195,7 @@ class FileUpload extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
return FormField::NO_SAVE_DATA;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -130,7 +130,7 @@ class RecordFinder extends FormWidgetBase
|
|||
|
||||
public function onRefresh()
|
||||
{
|
||||
list($model, $attribute) = $this->getModelArrayAttribute($this->valueFrom);
|
||||
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
|
||||
$model->{$attribute} = post($this->formField->getName());
|
||||
|
||||
$this->prepareVars();
|
||||
|
|
@ -142,8 +142,7 @@ class RecordFinder extends FormWidgetBase
|
|||
*/
|
||||
public function prepareVars()
|
||||
{
|
||||
// This should be a relation and return a Model
|
||||
$this->relationModel = $this->getLoadData();
|
||||
$this->relationModel = $this->getLoadValue();
|
||||
|
||||
$this->vars['value'] = $this->getKeyValue();
|
||||
$this->vars['field'] = $this->formField;
|
||||
|
|
@ -165,11 +164,25 @@ class RecordFinder extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
return strlen($value) ? $value : null;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getLoadValue()
|
||||
{
|
||||
list($model, $attribute) = $this->resolveModelAttribute($this->valueFrom);
|
||||
|
||||
if (!is_null($model)) {
|
||||
return $model->{$attribute};
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getKeyValue()
|
||||
{
|
||||
if (!$this->relationModel) {
|
||||
|
|
|
|||
|
|
@ -104,7 +104,7 @@ class Relation extends FormWidgetBase
|
|||
|
||||
$field = clone $this->formField;
|
||||
|
||||
list($model, $attribute) = $this->getModelArrayAttribute($this->relationName);
|
||||
list($model, $attribute) = $this->resolveModelAttribute($this->relationName);
|
||||
$relatedObj = $model->makeRelation($attribute);
|
||||
$query = $model->{$attribute}()->newQuery();
|
||||
|
||||
|
|
@ -141,7 +141,7 @@ class Relation extends FormWidgetBase
|
|||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
public function getSaveData($value)
|
||||
public function getSaveValue($value)
|
||||
{
|
||||
if (is_string($value) && !strlen($value)) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ class RichEditor extends FormWidgetBase
|
|||
$this->vars['stretch'] = $this->formField->stretch;
|
||||
$this->vars['size'] = $this->formField->size;
|
||||
$this->vars['name'] = $this->formField->getName();
|
||||
$this->vars['value'] = $this->getLoadData();
|
||||
$this->vars['value'] = $this->getLoadValue();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,10 +1,9 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
/**
|
||||
* Register Backend routes before all user routes.
|
||||
*/
|
||||
App::before(function ($request) {
|
||||
|
||||
/*
|
||||
* Other pages
|
||||
*/
|
||||
|
|
@ -16,5 +15,4 @@ App::before(function ($request) {
|
|||
* Entry point
|
||||
*/
|
||||
Route::any(Config::get('cms.backendUri', 'backend'), 'Backend\Classes\BackendController@run');
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php namespace Backend\Traits;
|
||||
|
||||
use Lang;
|
||||
use Backend\Classes\WidgetManager;
|
||||
use System\Classes\SystemException;
|
||||
|
||||
|
|
@ -19,14 +20,20 @@ trait WidgetMaker
|
|||
* Makes a widget object with the supplied configuration file.
|
||||
* @param string $class Widget class name
|
||||
* @param array $configuration An array of config.
|
||||
* @return WidgetBase The widget or null
|
||||
* @return WidgetBase The widget object
|
||||
*/
|
||||
public function makeWidget($class, $configuration = null)
|
||||
public function makeWidget($class, $configuration = [])
|
||||
{
|
||||
$controller = ($this->controller) ?: $this;
|
||||
$controller = property_exists($this, 'controller') && $this->controller
|
||||
? $this->controller
|
||||
: $this;
|
||||
|
||||
$manager = WidgetManager::instance();
|
||||
$widget = $manager->makeWidget($class, $controller, $configuration);
|
||||
return $widget;
|
||||
if (!class_exists($class)) {
|
||||
throw new SystemException(Lang::get('backend::lang.widget.not_registered', [
|
||||
'name' => $class
|
||||
]));
|
||||
}
|
||||
|
||||
return new $class($controller, $configuration);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -709,47 +709,11 @@ class Form extends WidgetBase
|
|||
$field = $this->fields[$field];
|
||||
}
|
||||
|
||||
$fieldName = $field->fieldName;
|
||||
$defaultValue = (!$this->model->exists && $field->defaults !== '') ? $field->defaults : null;
|
||||
$defaultValue = (!$this->model->exists && $field->defaults !== '')
|
||||
? $field->defaults
|
||||
: null;
|
||||
|
||||
/*
|
||||
* Array field name, eg: field[key][key2][key3]
|
||||
*/
|
||||
$keyParts = Str::evalHtmlArray($fieldName);
|
||||
$lastField = end($keyParts);
|
||||
$result = $this->data;
|
||||
|
||||
/*
|
||||
* Loop the field key parts and build a value.
|
||||
* To support relations only the last field should return the
|
||||
* relation value, all others will look up the relation object as normal.
|
||||
*/
|
||||
foreach ($keyParts as $key) {
|
||||
|
||||
if ($result instanceof Model && $result->hasRelation($key)) {
|
||||
if ($key == $lastField) {
|
||||
$result = $result->getRelationValue($key) ?: $defaultValue;
|
||||
}
|
||||
else {
|
||||
$result = $result->{$key};
|
||||
}
|
||||
}
|
||||
elseif (is_array($result)) {
|
||||
if (!array_key_exists($key, $result)) {
|
||||
return $defaultValue;
|
||||
}
|
||||
$result = $result[$key];
|
||||
}
|
||||
else {
|
||||
if (!isset($result->{$key})) {
|
||||
return $defaultValue;
|
||||
}
|
||||
$result = $result->{$key};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
return $result;
|
||||
return $field->getValueFromData($this->data, $defaultValue);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -806,7 +770,16 @@ class Form extends WidgetBase
|
|||
$parts = Str::evalHtmlArray($field);
|
||||
$dotted = implode('.', $parts);
|
||||
|
||||
$widgetValue = $widget->getSaveData(array_get($data, $dotted));
|
||||
$widgetValue = $widget->getSaveValue(array_get($data, $dotted));
|
||||
|
||||
/*
|
||||
* @deprecated Remove if year >= 2016
|
||||
*/
|
||||
if (method_exists($widget, 'getSaveData')) {
|
||||
traceLog('Method getSaveData() is deprecated, use getSaveValue() instead. Found in: ' . get_class($widget), 'warning');
|
||||
$widgetValue = $widget->getSaveData(array_get($data, $dotted));
|
||||
}
|
||||
|
||||
array_set($data, $dotted, $widgetValue);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,10 @@ class Table extends WidgetBase
|
|||
$this->dataSource = new $dataSourceClass($this->recordsKeyFrom);
|
||||
|
||||
if (Request::method() == 'POST' && $this->isClientDataSource()) {
|
||||
$requestDataField = $this->alias.'TableData';
|
||||
if ( strpos($this->alias, '[') === false )
|
||||
$requestDataField = $this->alias.'TableData';
|
||||
else
|
||||
$requestDataField = $this->alias.'[TableData]';
|
||||
|
||||
if (Request::exists($requestDataField)) {
|
||||
// Load data into the client memory data source on POST
|
||||
|
|
@ -135,7 +138,7 @@ class Table extends WidgetBase
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts the columns associative array to a regular array.
|
||||
* Converts the columns associative array to a regular array and translates column headers and drop-down options.
|
||||
* Working with regular arrays is much faster in JavaScript.
|
||||
* References:
|
||||
* - http://www.smashingmagazine.com/2012/11/05/writing-fast-memory-efficient-javascript/
|
||||
|
|
@ -147,6 +150,15 @@ class Table extends WidgetBase
|
|||
|
||||
foreach ($this->columns as $key=>$data) {
|
||||
$data['key'] = $key;
|
||||
|
||||
if (isset($data['title']))
|
||||
$data['title'] = trans($data['title']);
|
||||
|
||||
if (isset($data['options'])) {
|
||||
foreach ($data['options'] as &$option)
|
||||
$option = trans($option);
|
||||
}
|
||||
|
||||
$result[] = $data;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,13 +2,19 @@
|
|||
* General control styling
|
||||
*/
|
||||
.control-table .table-container {
|
||||
border: 1px solid #808c8d;
|
||||
border: 1px solid #e0e0e0;
|
||||
-webkit-border-radius: 4px;
|
||||
-moz-border-radius: 4px;
|
||||
border-radius: 4px;
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.control-table .table-container:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
.control-table.active .table-container {
|
||||
border-color: #808c8d;
|
||||
}
|
||||
.control-table table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
|
|
@ -35,7 +41,7 @@
|
|||
left: 1px;
|
||||
right: 1px;
|
||||
margin-top: -1px;
|
||||
border-bottom: 1px solid #bdc3c7;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
.control-table table.headers th {
|
||||
padding: 7px 10px;
|
||||
|
|
@ -55,9 +61,11 @@
|
|||
.control-table table.headers th:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
.control-table.active table.headers:after {
|
||||
border-bottom-color: #808c8d;
|
||||
}
|
||||
.control-table table.data td {
|
||||
border: 1px solid #ecf0f1;
|
||||
/* TODO: this should be applied only when the control is active */
|
||||
}
|
||||
.control-table table.data td .content-container {
|
||||
position: relative;
|
||||
|
|
@ -114,7 +122,7 @@
|
|||
}
|
||||
.control-table .toolbar {
|
||||
background: white;
|
||||
border-bottom: 1px solid #bdc3c7;
|
||||
border-bottom: 1px solid #e0e0e0;
|
||||
}
|
||||
.control-table .toolbar a {
|
||||
color: #323e50;
|
||||
|
|
@ -144,6 +152,9 @@
|
|||
.control-table .toolbar a.delete-table-row:before {
|
||||
background-position: 0 -113px;
|
||||
}
|
||||
.control-table.active .toolbar {
|
||||
border-bottom-color: #808c8d;
|
||||
}
|
||||
.control-table .pagination ul {
|
||||
padding: 0;
|
||||
margin-bottom: 15px;
|
||||
|
|
@ -304,6 +315,7 @@ html.cssanimations .control-table td[data-column-type=dropdown] [data-view-conta
|
|||
border-top: none;
|
||||
padding-top: 1px;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
-webkit-box-sizing: border-box;
|
||||
-moz-box-sizing: border-box;
|
||||
box-sizing: border-box;
|
||||
|
|
|
|||
|
|
@ -65,6 +65,7 @@
|
|||
// Event handlers
|
||||
this.clickHandler = this.onClick.bind(this)
|
||||
this.keydownHandler = this.onKeydown.bind(this)
|
||||
this.documentClickHandler = this.onDocumentClick.bind(this)
|
||||
this.toolbarClickHandler = this.onToolbarClick.bind(this)
|
||||
|
||||
if (this.options.postback && this.options.clientDataSourceClass == 'client')
|
||||
|
|
@ -135,6 +136,7 @@
|
|||
Table.prototype.registerHandlers = function() {
|
||||
this.el.addEventListener('click', this.clickHandler)
|
||||
this.el.addEventListener('keydown', this.keydownHandler)
|
||||
document.addEventListener('click', this.documentClickHandler)
|
||||
|
||||
if (this.options.postback && this.options.clientDataSourceClass == 'client')
|
||||
this.$el.closest('form').bind('oc.beforeRequest', this.formSubmitHandler)
|
||||
|
|
@ -146,6 +148,8 @@
|
|||
|
||||
Table.prototype.unregisterHandlers = function() {
|
||||
this.el.removeEventListener('click', this.clickHandler);
|
||||
document.removeEventListener('click', this.documentClickHandler)
|
||||
|
||||
this.clickHandler = null
|
||||
|
||||
this.el.removeEventListener('keydown', this.keydownHandler);
|
||||
|
|
@ -449,6 +453,8 @@
|
|||
* Removes editor from the currently edited cell and commits the row if needed.
|
||||
*/
|
||||
Table.prototype.unfocusTable = function() {
|
||||
this.elementRemoveClass(this.el, 'active')
|
||||
|
||||
if (this.activeCellProcessor)
|
||||
this.activeCellProcessor.onUnfocus()
|
||||
|
||||
|
|
@ -461,6 +467,13 @@
|
|||
this.activeCell = null
|
||||
}
|
||||
|
||||
/*
|
||||
* Makes the table focused in the UI
|
||||
*/
|
||||
Table.prototype.focusTable = function() {
|
||||
this.elementAddClass(this.el, 'active')
|
||||
}
|
||||
|
||||
/*
|
||||
* Calls the onFocus() method for the cell processor responsible for the
|
||||
* newly focused cell. Commit the previous edited row to the data source
|
||||
|
|
@ -471,6 +484,8 @@
|
|||
if (columnName === null)
|
||||
return
|
||||
|
||||
this.focusTable()
|
||||
|
||||
var processor = this.getCellProcessor(columnName)
|
||||
if (!processor)
|
||||
throw new Error("Cell processor not found for the column "+columnName)
|
||||
|
|
@ -615,6 +630,8 @@
|
|||
// ============================
|
||||
|
||||
Table.prototype.onClick = function(ev) {
|
||||
this.focusTable()
|
||||
|
||||
if (this.navigation.onClick(ev) === false)
|
||||
return
|
||||
|
||||
|
|
@ -670,7 +687,12 @@
|
|||
Table.prototype.onFormSubmit = function(ev, data) {
|
||||
if (data.handler == this.options.postbackHandlerName) {
|
||||
this.unfocusTable()
|
||||
data.options.data[this.options.alias + 'TableData'] = this.dataSource.getAllData()
|
||||
|
||||
var fieldName = this.options.alias.indexOf('[') > -1 ?
|
||||
this.options.alias + '[TableData]' :
|
||||
this.options.alias + 'TableData';
|
||||
|
||||
data.options.data[fieldName] = this.dataSource.getAllData()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -693,6 +715,24 @@
|
|||
this.stopEvent(ev)
|
||||
}
|
||||
|
||||
Table.prototype.onDocumentClick = function(ev) {
|
||||
var target = this.getEventTarget(ev)
|
||||
|
||||
// Determine if the click was inside the table element
|
||||
// and just exit if so
|
||||
if (this.parentContainsElement(this.el, target))
|
||||
return
|
||||
|
||||
// Request the active cell processor if the clicked
|
||||
// element belongs to any extra-table element created
|
||||
// by the processor
|
||||
|
||||
if (this.activeCellProcessor && this.activeCellProcessor.elementBelongsToProcessor(target))
|
||||
return
|
||||
|
||||
this.unfocusTable()
|
||||
}
|
||||
|
||||
// PUBLIC METHODS
|
||||
// ============================
|
||||
|
||||
|
|
@ -790,6 +830,44 @@
|
|||
ev.returnValue = false
|
||||
}
|
||||
|
||||
Table.prototype.elementHasClass = function(el, className) {
|
||||
// TODO: refactor to a core library
|
||||
|
||||
if (el.classList)
|
||||
return el.classList.contains(className);
|
||||
|
||||
return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
|
||||
}
|
||||
|
||||
Table.prototype.elementAddClass = function(el, className) {
|
||||
// TODO: refactor to a core library
|
||||
|
||||
if (this.elementHasClass(el, className))
|
||||
return
|
||||
|
||||
if (el.classList)
|
||||
el.classList.add(className);
|
||||
else
|
||||
el.className += ' ' + className;
|
||||
}
|
||||
|
||||
Table.prototype.elementRemoveClass = function(el, className) {
|
||||
// TODO: refactor to a core library
|
||||
|
||||
if (el.classList)
|
||||
el.classList.remove(className);
|
||||
else
|
||||
el.className = el.className.replace(new RegExp('(^|\\b)' + className.split(' ').join('|') + '(\\b|$)', 'gi'), ' ');
|
||||
}
|
||||
|
||||
Table.prototype.parentContainsElement = function(parent, element) {
|
||||
while (element && element != parent) {
|
||||
element = element.parentNode
|
||||
}
|
||||
|
||||
return element ? true : false
|
||||
}
|
||||
|
||||
Table.prototype.getCellValue = function(cellElement) {
|
||||
return cellElement.querySelector('[data-container]').value
|
||||
}
|
||||
|
|
|
|||
|
|
@ -167,5 +167,13 @@
|
|||
return this.getViewContainer(cellElement).textContent = value
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the specified element is some element created by the
|
||||
* processor.
|
||||
*/
|
||||
Base.prototype.elementBelongsToProcessor = function(element) {
|
||||
return false
|
||||
}
|
||||
|
||||
$.oc.table.processor.base = Base
|
||||
}(window.jQuery);
|
||||
|
|
@ -246,7 +246,7 @@
|
|||
DropdownProcessor.prototype.getAbsolutePosition = function(element) {
|
||||
// TODO: refactor to a core library
|
||||
|
||||
var top = 0,
|
||||
var top = document.body.scrollTop,
|
||||
left = 0
|
||||
|
||||
do {
|
||||
|
|
@ -344,7 +344,7 @@
|
|||
/*
|
||||
* This method is called when a cell value in the row changes.
|
||||
*/
|
||||
Base.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
DropdownProcessor.prototype.onRowValueChanged = function(columnName, cellElement) {
|
||||
// Determine if this drop-down depends on the changed column
|
||||
// and update the option list if necessary
|
||||
|
||||
|
|
@ -382,5 +382,16 @@
|
|||
})
|
||||
}
|
||||
|
||||
/*
|
||||
* Determines whether the specified element is some element created by the
|
||||
* processor.
|
||||
*/
|
||||
DropdownProcessor.prototype.elementBelongsToProcessor = function(element) {
|
||||
if (!this.itemListElement)
|
||||
return false
|
||||
|
||||
return this.tableObj.parentContainsElement(this.itemListElement, element)
|
||||
}
|
||||
|
||||
$.oc.table.processor.dropdown = DropdownProcessor;
|
||||
}(window.jQuery);
|
||||
|
|
@ -4,12 +4,23 @@
|
|||
* General control styling
|
||||
*/
|
||||
|
||||
@table-active-border: #808c8d;
|
||||
@table-inactive-border: #e0e0e0;
|
||||
|
||||
.control-table {
|
||||
.table-container {
|
||||
border: 1px solid #808c8d;
|
||||
border: 1px solid @table-inactive-border;
|
||||
.border-radius(4px);
|
||||
overflow: hidden;
|
||||
margin-bottom: 15px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.active .table-container {
|
||||
border-color: @table-active-border;
|
||||
}
|
||||
|
||||
table {
|
||||
|
|
@ -41,7 +52,7 @@
|
|||
left: 1px;
|
||||
right: 1px;
|
||||
margin-top: -1px;
|
||||
border-bottom: 1px solid #bdc3c7;
|
||||
border-bottom: 1px solid @table-inactive-border;
|
||||
}
|
||||
|
||||
th {
|
||||
|
|
@ -66,6 +77,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.active table.headers:after {
|
||||
border-bottom-color: @table-active-border;
|
||||
}
|
||||
|
||||
table.data {
|
||||
td {
|
||||
border: 1px solid #ecf0f1;
|
||||
|
|
@ -75,7 +90,6 @@
|
|||
padding: 1px;
|
||||
}
|
||||
|
||||
/* TODO: this should be applied only when the control is active */
|
||||
&.active {
|
||||
border-color: @color-focus!important;
|
||||
|
||||
|
|
@ -146,7 +160,7 @@
|
|||
|
||||
.toolbar {
|
||||
background: white;
|
||||
border-bottom: 1px solid #bdc3c7;
|
||||
border-bottom: 1px solid @table-inactive-border;
|
||||
|
||||
a {
|
||||
color: #323e50;
|
||||
|
|
@ -180,6 +194,10 @@
|
|||
}
|
||||
}
|
||||
|
||||
&.active .toolbar {
|
||||
border-bottom-color: @table-active-border;
|
||||
}
|
||||
|
||||
.pagination {
|
||||
ul {
|
||||
padding: 0;
|
||||
|
|
@ -354,10 +372,11 @@ html.cssanimations {
|
|||
.user-select(none);
|
||||
position: absolute;
|
||||
background: white;
|
||||
border: 1px solid #808c8d;
|
||||
border: 1px solid @table-active-border;
|
||||
border-top: none;
|
||||
padding-top: 1px;
|
||||
overflow: hidden;
|
||||
z-index: 1000;
|
||||
.box-sizing(border-box);
|
||||
.border-bottom-radius(4px);
|
||||
|
||||
|
|
|
|||
|
|
@ -80,7 +80,7 @@
|
|||
})
|
||||
|
||||
/*
|
||||
* Listen for the closed event
|
||||
* Listen for the closing events
|
||||
*/
|
||||
$('#cms-master-tabs').on('closed.oc.tab', function(event){
|
||||
updateModifiedCounter()
|
||||
|
|
@ -89,6 +89,11 @@
|
|||
setPageTitle('')
|
||||
})
|
||||
|
||||
$('#cms-master-tabs').on('beforeClose.oc.tab', function(event){
|
||||
// Dispose data table widgets
|
||||
$('[data-control=table]', event.relatedTarget).table('dispose')
|
||||
})
|
||||
|
||||
/*
|
||||
* Listen for the onBeforeRequest event
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use Exception;
|
|||
*/
|
||||
class CmsException extends ApplicationException
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Cms\Classes\CmsCompoundObject A reference to a CMS object used for masking errors.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -10,7 +10,6 @@ use System\Classes\ApplicationException;
|
|||
*/
|
||||
class CmsObjectQuery
|
||||
{
|
||||
|
||||
protected $useCache = false;
|
||||
|
||||
protected $cmsObject;
|
||||
|
|
|
|||
|
|
@ -8,6 +8,7 @@ use View;
|
|||
use Lang;
|
||||
use Event;
|
||||
use Config;
|
||||
use Session;
|
||||
use Request;
|
||||
use Response;
|
||||
use Exception;
|
||||
|
|
@ -235,6 +236,13 @@ class Controller extends BaseController
|
|||
'environment' => App::environment(),
|
||||
];
|
||||
|
||||
/*
|
||||
* Check for the presence of validation errors in the session.
|
||||
*/
|
||||
$this->vars['errors'] = (Config::get('session.driver') && Session::has('errors'))
|
||||
? Session::get('errors')
|
||||
: new \Illuminate\Support\ViewErrorBag;
|
||||
|
||||
/*
|
||||
* Handle AJAX requests and execute the life cycle functions
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php namespace Cms\Classes;
|
||||
|
||||
use Str;
|
||||
|
||||
/**
|
||||
* This class parses CMS object files (pages, partials and layouts).
|
||||
* Returns the structured file information.
|
||||
|
|
@ -72,6 +74,7 @@ class SectionParser
|
|||
*/
|
||||
public static function parseOffset($content)
|
||||
{
|
||||
$content = Str::normalizeEol($content);
|
||||
$sections = preg_split('/^={2,}\s*/m', $content, -1);
|
||||
$count = count($sections);
|
||||
|
||||
|
|
|
|||
|
|
@ -349,6 +349,8 @@ class Index extends Controller
|
|||
throw new ApplicationException(trans('cms::lang.template.not_found'));
|
||||
}
|
||||
|
||||
Event::fire('cms.template.processSettingsAfterLoad', [$this, $template]);
|
||||
|
||||
return $template;
|
||||
}
|
||||
|
||||
|
|
@ -443,7 +445,13 @@ class Index extends Controller
|
|||
if (array_key_exists('viewBag', $_POST))
|
||||
$settings['viewBag'] = $_POST['viewBag'];
|
||||
|
||||
return $settings;
|
||||
$dataHolder = (object)[
|
||||
'settings' => $settings
|
||||
];
|
||||
|
||||
Event::fire('cms.template.processSettingsBeforeSave', [$this, $dataHolder]);
|
||||
|
||||
return $dataHolder->settings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,14 +1,12 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
/**
|
||||
* Register CMS routes before all user routes.
|
||||
*/
|
||||
App::before(function ($request) {
|
||||
|
||||
/*
|
||||
* The CMS module intercepts all URLs that were not
|
||||
* The CMS module intercepts all URLs that were not
|
||||
* handled by the back-end modules.
|
||||
*/
|
||||
Route::any('{slug}', 'Cms\Classes\Controller@run')->where('slug', '(.*)?');
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
<?php namespace System\Behaviors;
|
||||
|
||||
use Cache;
|
||||
use DbDongle;
|
||||
use System\Classes\ModelBehavior;
|
||||
use System\Classes\ApplicationException;
|
||||
|
||||
|
|
@ -100,7 +101,7 @@ class SettingsModel extends ModelBehavior
|
|||
*/
|
||||
public function isConfigured()
|
||||
{
|
||||
return $this->getSettingsRecord() !== null;
|
||||
return DbDongle::hasDatabase() && $this->getSettingsRecord() !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ use Exception;
|
|||
*/
|
||||
class Controller extends BaseController
|
||||
{
|
||||
|
||||
/**
|
||||
* Combines JavaScript and StyleSheet assets.
|
||||
* @param string $name Combined file code
|
||||
|
|
|
|||
|
|
@ -19,7 +19,6 @@ use System\Classes\ApplicationException;
|
|||
*/
|
||||
class ErrorHandler
|
||||
{
|
||||
|
||||
/**
|
||||
* @var System\Classes\ExceptionBase A prepared mask exception used to mask any exception fired.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -16,7 +16,6 @@ use System\Classes\ApplicationException;
|
|||
*/
|
||||
class ExceptionBase extends Exception
|
||||
{
|
||||
|
||||
/**
|
||||
* @var Exception If this exception is acting as a mask, this property stores the face exception.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -12,7 +12,6 @@ use October\Rain\Database\ModelBehavior as ModelBehaviorBase;
|
|||
*/
|
||||
class ModelBehavior extends ModelBehaviorBase
|
||||
{
|
||||
|
||||
/**
|
||||
* @var array Properties that must exist in the model using this behavior.
|
||||
*/
|
||||
|
|
@ -29,7 +28,8 @@ class ModelBehavior extends ModelBehaviorBase
|
|||
/*
|
||||
* Validate model properties
|
||||
*/
|
||||
foreach ($this->requiredProperties as $property) {
|
||||
foreach ($this->requiredProperties as $property)
|
||||
{
|
||||
if (!isset($model->{$property})) {
|
||||
throw new ApplicationException(Lang::get('system::lang.behavior.missing_property', [
|
||||
'class' => get_class($model),
|
||||
|
|
|
|||
|
|
@ -211,7 +211,6 @@ class PluginManager
|
|||
|
||||
/**
|
||||
* Returns the directory path to a plugin
|
||||
*
|
||||
*/
|
||||
public function getPluginPath($id)
|
||||
{
|
||||
|
|
@ -220,7 +219,7 @@ class PluginManager
|
|||
return null;
|
||||
}
|
||||
|
||||
return $this->pathMap[$classId];
|
||||
return File::normalizePath($this->pathMap[$classId]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?php namespace System\Classes;
|
||||
|
||||
use App;
|
||||
use Log;
|
||||
|
||||
/**
|
||||
|
|
@ -14,6 +15,12 @@ class SystemException extends ExceptionBase
|
|||
public function __construct($message = "", $code = 0, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct($message, $code, $previous);
|
||||
Log::error($this);
|
||||
|
||||
/*
|
||||
* Log the exception
|
||||
*/
|
||||
if (!App::runningUnitTests()) {
|
||||
Log::error($this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ use Exception;
|
|||
*
|
||||
* @package october\system
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class EventLogs extends Controller
|
||||
{
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
'Backend.Behaviors.ListController'
|
||||
|
|
|
|||
|
|
@ -17,11 +17,9 @@ use Exception;
|
|||
*
|
||||
* @package october\system
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class MailLayouts extends Controller
|
||||
{
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
];
|
||||
|
|
|
|||
|
|
@ -20,11 +20,9 @@ use Exception;
|
|||
*
|
||||
* @package october\system
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class MailTemplates extends Controller
|
||||
{
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
'Backend.Behaviors.ListController'
|
||||
|
|
|
|||
|
|
@ -18,11 +18,9 @@ use Exception;
|
|||
*
|
||||
* @package october\system
|
||||
* @author Alexey Bobkov, Samuel Georges
|
||||
*
|
||||
*/
|
||||
class RequestLogs extends Controller
|
||||
{
|
||||
|
||||
public $implement = [
|
||||
'Backend.Behaviors.FormController',
|
||||
'Backend.Behaviors.ListController'
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use Model;
|
|||
*/
|
||||
class EventLog extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -11,7 +11,6 @@ use Request;
|
|||
*/
|
||||
class RequestLog extends Model
|
||||
{
|
||||
|
||||
/**
|
||||
* @var string The database table used by the model.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
/**
|
||||
* Register System routes before all user routes.
|
||||
*/
|
||||
App::before(function ($request) {
|
||||
|
||||
/*
|
||||
* Combine JavaScript and StyleSheet assets
|
||||
*/
|
||||
Route::any('combine/{file}', 'System\Classes\Controller@combine');
|
||||
|
||||
});
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@
|
|||
>
|
||||
<testsuites>
|
||||
<testsuite name="October CMS Test Suite">
|
||||
<directory>./tests</directory>
|
||||
<directory>./tests/unit</directory>
|
||||
</testsuite>
|
||||
<testsuite name="October Rain Test Suite">
|
||||
<directory>./vendor/october/rain/tests</directory>
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<phpunit backupGlobals="false"
|
||||
backupStaticAttributes="false"
|
||||
bootstrap="../../../autoload.php"
|
||||
bootstrap="../../bootstrap/autoload.php"
|
||||
colors="true"
|
||||
convertErrorsToExceptions="true"
|
||||
convertNoticesToExceptions="true"
|
||||
|
|
@ -5,23 +5,6 @@ use Backend\Classes\WidgetManager;
|
|||
|
||||
class WidgetManagerTest extends TestCase
|
||||
{
|
||||
public function testMakeWidget()
|
||||
{
|
||||
$manager = WidgetManager::instance();
|
||||
$widget = $manager->makeWidget('Backend\Widgets\Search');
|
||||
$this->assertTrue($widget instanceof \Backend\Widgets\Search);
|
||||
|
||||
$controller = new Controller;
|
||||
$widget = $manager->makeWidget('Backend\Widgets\Search', $controller);
|
||||
$this->assertInstanceOf('Backend\Widgets\Search', $widget);
|
||||
$this->assertInstanceOf('Backend\Classes\Controller', $widget->getController());
|
||||
|
||||
$config = ['test' => 'config'];
|
||||
$widget = $manager->makeWidget('Backend\Widgets\Search', null, $config);
|
||||
$this->assertInstanceOf('Backend\Widgets\Search', $widget);
|
||||
$this->assertEquals('config', $widget->getConfig('test'));
|
||||
}
|
||||
|
||||
public function testListFormWidgets()
|
||||
{
|
||||
$manager = WidgetManager::instance();
|
||||
|
|
|
|||
|
|
@ -0,0 +1,60 @@
|
|||
<?php
|
||||
|
||||
use Backend\Classes\Controller;
|
||||
|
||||
class ExampleTraitClass
|
||||
{
|
||||
use \Backend\Traits\WidgetMaker;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->controller = new Controller;
|
||||
}
|
||||
}
|
||||
|
||||
class WidgetMakerTest extends TestCase
|
||||
{
|
||||
/**
|
||||
* The object under test.
|
||||
*
|
||||
* @var object
|
||||
*/
|
||||
private $traitObject;
|
||||
|
||||
/**
|
||||
* Sets up the fixture.
|
||||
*
|
||||
* This method is called before a test is executed.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setUp()
|
||||
{
|
||||
$traitName = 'Backend\Traits\WidgetMaker';
|
||||
$this->traitObject = $this->getObjectForTrait($traitName);
|
||||
}
|
||||
|
||||
public function testTraitObject()
|
||||
{
|
||||
$maker = $this->traitObject;
|
||||
|
||||
$widget = $maker->makeWidget('Backend\Widgets\Search');
|
||||
$this->assertTrue($widget instanceof \Backend\Widgets\Search);
|
||||
}
|
||||
|
||||
public function testMakeWidget()
|
||||
{
|
||||
$manager = new ExampleTraitClass;
|
||||
|
||||
$controller = new Controller;
|
||||
$widget = $manager->makeWidget('Backend\Widgets\Search');
|
||||
$this->assertInstanceOf('Backend\Widgets\Search', $widget);
|
||||
$this->assertInstanceOf('Backend\Classes\Controller', $widget->getController());
|
||||
|
||||
$config = ['test' => 'config'];
|
||||
$widget = $manager->makeWidget('Backend\Widgets\Search', $config);
|
||||
$this->assertInstanceOf('Backend\Widgets\Search', $widget);
|
||||
$this->assertEquals('config', $widget->getConfig('test'));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -162,7 +162,7 @@ class CmsCompoundObjectTest extends TestCase
|
|||
$this->assertFileExists($referenceFilePath);
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertFileEquals($referenceFilePath, $destFilePath);
|
||||
$this->assertFileEqualsNormalized($referenceFilePath, $destFilePath);
|
||||
}
|
||||
|
||||
public function testSaveMarkupAndSettings()
|
||||
|
|
@ -187,7 +187,7 @@ class CmsCompoundObjectTest extends TestCase
|
|||
$this->assertFileExists($referenceFilePath);
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertFileEquals($referenceFilePath, $destFilePath);
|
||||
$this->assertFileEqualsNormalized($referenceFilePath, $destFilePath);
|
||||
}
|
||||
|
||||
public function testSaveFull()
|
||||
|
|
@ -195,8 +195,9 @@ class CmsCompoundObjectTest extends TestCase
|
|||
$theme = Theme::load('apitest');
|
||||
|
||||
$destFilePath = $theme->getPath().'/testobjects/compound.htm';
|
||||
if (file_exists($destFilePath))
|
||||
if (file_exists($destFilePath)) {
|
||||
unlink($destFilePath);
|
||||
}
|
||||
|
||||
$this->assertFileNotExists($destFilePath);
|
||||
|
||||
|
|
@ -213,6 +214,22 @@ class CmsCompoundObjectTest extends TestCase
|
|||
$this->assertFileExists($referenceFilePath);
|
||||
|
||||
$this->assertFileExists($destFilePath);
|
||||
$this->assertFileEquals($referenceFilePath, $destFilePath);
|
||||
$this->assertFileEqualsNormalized($referenceFilePath, $destFilePath);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function assertFileEqualsNormalized($expected, $actual)
|
||||
{
|
||||
$expected = file_get_contents($expected);
|
||||
$expected = preg_replace('~\R~u', PHP_EOL, $expected); // Normalize EOL
|
||||
|
||||
$actual = file_get_contents($actual);
|
||||
$actual = preg_replace('~\R~u', PHP_EOL, $actual); // Normalize EOL
|
||||
|
||||
$this->assertEquals($expected, $actual);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -141,7 +141,7 @@ class CmsObjectTest extends TestCase
|
|||
|
||||
/**
|
||||
* @expectedException \System\Classes\ApplicationException
|
||||
* @expectedExceptionMessage The property "something" cannot be set
|
||||
* @expectedExceptionMessage The property 'something' cannot be set
|
||||
*/
|
||||
public function testFillNotFillable()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -253,10 +253,22 @@ class CodeParserTest extends TestCase
|
|||
|
||||
$referenceFilePath = base_path().'/tests/fixtures/cms/reference/namespaces.php';
|
||||
$this->assertFileExists($referenceFilePath);
|
||||
$referenceContents = file_get_contents($referenceFilePath);
|
||||
$referenceContents = $this->getContents($referenceFilePath);
|
||||
|
||||
$referenceContents = str_replace('{className}', $info['className'], $referenceContents);
|
||||
|
||||
$this->assertEquals($referenceContents, file_get_contents($info['filePath']));
|
||||
$this->assertEquals($referenceContents, $this->getContents($info['filePath']));
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function getContents($path)
|
||||
{
|
||||
$content = file_get_contents($path);
|
||||
$content = preg_replace('~\R~u', PHP_EOL, $content); // Normalize EOL
|
||||
return $content;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -28,7 +28,7 @@ class FileHelperTest extends TestCase
|
|||
|
||||
$str = FileHelper::formatIniString($data);
|
||||
$this->assertNotEmpty($str);
|
||||
$this->assertEquals(file_get_contents($path), $str);
|
||||
$this->assertEquals($this->getContents($path), $str);
|
||||
|
||||
$data = [
|
||||
'section' => [
|
||||
|
|
@ -47,7 +47,7 @@ class FileHelperTest extends TestCase
|
|||
$this->assertFileExists($path);
|
||||
|
||||
$str = FileHelper::formatIniString($data);
|
||||
$this->assertEquals(file_get_contents($path), $str);
|
||||
$this->assertEquals($this->getContents($path), $str);
|
||||
|
||||
$data = [
|
||||
'section' => [
|
||||
|
|
@ -75,6 +75,18 @@ class FileHelperTest extends TestCase
|
|||
$this->assertFileExists($path);
|
||||
|
||||
$str = FileHelper::formatIniString($data);
|
||||
$this->assertEquals(file_get_contents($path), $str);
|
||||
$this->assertEquals($this->getContents($path), $str);
|
||||
}
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
|
||||
protected function getContents($path)
|
||||
{
|
||||
$content = file_get_contents($path);
|
||||
$content = preg_replace('~\R~u', PHP_EOL, $content); // Normalize EOL
|
||||
return $content;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,11 +6,10 @@ use Cms\Classes\Theme;
|
|||
class RouterTest extends TestCase
|
||||
{
|
||||
protected static $theme = null;
|
||||
|
||||
|
||||
public static function setUpBeforeClass()
|
||||
{
|
||||
self::$theme = new Theme();
|
||||
self::$theme->load('test');
|
||||
self::$theme = Theme::load('test');
|
||||
}
|
||||
|
||||
protected static function getMethod($name)
|
||||
|
|
@ -28,7 +27,7 @@ class RouterTest extends TestCase
|
|||
$property->setAccessible(true);
|
||||
return $property;
|
||||
}
|
||||
|
||||
|
||||
public function testLoadUrlMap()
|
||||
{
|
||||
$method = self::getMethod('loadUrlMap');
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
use Cms\Classes\Theme;
|
||||
|
||||
class ThemeTest extends TestCase
|
||||
class ThemeTest extends TestCase
|
||||
{
|
||||
public function setUp()
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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="false"
|
||||
syntaxCheck="false"
|
||||
>
|
||||
<testsuites>
|
||||
<testsuite name="October Test Suite">
|
||||
<directory>./</directory>
|
||||
</testsuite>
|
||||
</testsuites>
|
||||
</phpunit>
|
||||
|
|
@ -72,7 +72,8 @@ class PluginManagerTest extends TestCase
|
|||
{
|
||||
$manager = PluginManager::instance();
|
||||
$result = $manager->getPluginPath('October\Tester');
|
||||
$this->assertEquals(base_path() . '/tests/fixtures/system/plugins/october/tester', $result);
|
||||
$basePath = str_replace('\\', '/', base_path());
|
||||
$this->assertEquals($basePath . '/tests/fixtures/system/plugins/october/tester', $result);
|
||||
}
|
||||
|
||||
public function testGetPlugins()
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ description = "Default layout"
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>October CMS - {{ this.page.title }}</title>
|
||||
<meta name="author" content="October CMS">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
|
@ -60,4 +61,4 @@ description = "Default layout"
|
|||
{% scripts %}
|
||||
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue