Merge branch 'develop' of github.com:octobercms/october into develop

This commit is contained in:
alekseybobkov 2014-08-02 11:25:11 +11:00
commit d562e68b91
86 changed files with 1093 additions and 196 deletions

View File

@ -2,6 +2,17 @@
return array(
/*
|--------------------------------------------------------------------------
| Specifies the default CMS theme
|--------------------------------------------------------------------------
|
| This parameter value can be overridden by the CMS back-end settings.
|
*/
'activeTheme' => 'test',
/*
|--------------------------------------------------------------------------
| Plugins directory
@ -66,14 +77,16 @@ return array(
/*
|--------------------------------------------------------------------------
| Specifies the default CMS theme
| Determines if a friendly error page should be used.
|--------------------------------------------------------------------------
|
| This parameter value can be overridden by the CMS back-end settings.
| If this value is set to true, a friendly error page is used when an
| exception is encountered. You must create a CMS page with route "/error"
| to set the contents of this page. Otherwise the default error page is shown.
|
*/
'activeTheme' => 'test',
'customErrorPage' => true,
/*
|--------------------------------------------------------------------------
@ -88,7 +101,7 @@ return array(
*/
'enableAssetCache' => false,
/*
|--------------------------------------------------------------------------
| Disables Twig caching for unit tests

View File

@ -104,6 +104,15 @@ class ServiceProvider extends ModuleServiceProvider
'context' => 'mysettings',
'keywords' => 'backend::lang.myaccount.menu_keywords',
],
'access_logs' => [
'label' => 'backend::lang.access_log.menu_label',
'description' => 'backend::lang.access_log.menu_description',
'category' => 'Logs',
'icon' => 'icon-lock',
'url' => Backend::url('backend/accesslogs'),
'permissions' => ['backend.access_admin_logs'],
'order' => 800
],
]);
});

View File

@ -9178,151 +9178,151 @@ table.table.data .list-tree a.list-expand-collapse {
table.table.data tr.list-tree-level-1 a.list-expand-collapse {
left: 30px;
}
table.table.data tr.list-tree-level-1 td.list-data-column-1 {
table.table.data tr.list-tree-level-1 td.list-cell-index-1 {
padding-left: 35px;
}
table.table.data tr.list-tree-level-2 a.list-expand-collapse {
left: 40px;
}
table.table.data tr.list-tree-level-2 td.list-data-column-1 {
table.table.data tr.list-tree-level-2 td.list-cell-index-1 {
padding-left: 45px;
}
table.table.data tr.list-tree-level-3 a.list-expand-collapse {
left: 50px;
}
table.table.data tr.list-tree-level-3 td.list-data-column-1 {
table.table.data tr.list-tree-level-3 td.list-cell-index-1 {
padding-left: 55px;
}
table.table.data tr.list-tree-level-4 a.list-expand-collapse {
left: 60px;
}
table.table.data tr.list-tree-level-4 td.list-data-column-1 {
table.table.data tr.list-tree-level-4 td.list-cell-index-1 {
padding-left: 65px;
}
table.table.data tr.list-tree-level-5 a.list-expand-collapse {
left: 70px;
}
table.table.data tr.list-tree-level-5 td.list-data-column-1 {
table.table.data tr.list-tree-level-5 td.list-cell-index-1 {
padding-left: 75px;
}
table.table.data tr.list-tree-level-6 a.list-expand-collapse {
left: 80px;
}
table.table.data tr.list-tree-level-6 td.list-data-column-1 {
table.table.data tr.list-tree-level-6 td.list-cell-index-1 {
padding-left: 85px;
}
table.table.data tr.list-tree-level-7 a.list-expand-collapse {
left: 90px;
}
table.table.data tr.list-tree-level-7 td.list-data-column-1 {
table.table.data tr.list-tree-level-7 td.list-cell-index-1 {
padding-left: 95px;
}
table.table.data tr.list-tree-level-8 a.list-expand-collapse {
left: 100px;
}
table.table.data tr.list-tree-level-8 td.list-data-column-1 {
table.table.data tr.list-tree-level-8 td.list-cell-index-1 {
padding-left: 105px;
}
table.table.data tr.list-tree-level-9 a.list-expand-collapse {
left: 110px;
}
table.table.data tr.list-tree-level-9 td.list-data-column-1 {
table.table.data tr.list-tree-level-9 td.list-cell-index-1 {
padding-left: 115px;
}
table.table.data tr.list-tree-level-10 a.list-expand-collapse {
left: 120px;
}
table.table.data tr.list-tree-level-10 td.list-data-column-1 {
table.table.data tr.list-tree-level-10 td.list-cell-index-1 {
padding-left: 125px;
}
table.table.data tr.list-tree-level-11 a.list-expand-collapse {
left: 130px;
}
table.table.data tr.list-tree-level-11 td.list-data-column-1 {
table.table.data tr.list-tree-level-11 td.list-cell-index-1 {
padding-left: 135px;
}
table.table.data tr.list-tree-level-12 a.list-expand-collapse {
left: 140px;
}
table.table.data tr.list-tree-level-12 td.list-data-column-1 {
table.table.data tr.list-tree-level-12 td.list-cell-index-1 {
padding-left: 145px;
}
table.table.data tr.list-tree-level-13 a.list-expand-collapse {
left: 150px;
}
table.table.data tr.list-tree-level-13 td.list-data-column-1 {
table.table.data tr.list-tree-level-13 td.list-cell-index-1 {
padding-left: 155px;
}
table.table.data tr.list-tree-level-14 a.list-expand-collapse {
left: 160px;
}
table.table.data tr.list-tree-level-14 td.list-data-column-1 {
table.table.data tr.list-tree-level-14 td.list-cell-index-1 {
padding-left: 165px;
}
table.table.data tr.list-tree-level-15 a.list-expand-collapse {
left: 170px;
}
table.table.data tr.list-tree-level-15 td.list-data-column-1 {
table.table.data tr.list-tree-level-15 td.list-cell-index-1 {
padding-left: 175px;
}
table.table.data tr.list-tree-level-16 a.list-expand-collapse {
left: 180px;
}
table.table.data tr.list-tree-level-16 td.list-data-column-1 {
table.table.data tr.list-tree-level-16 td.list-cell-index-1 {
padding-left: 185px;
}
table.table.data tr.list-tree-level-17 a.list-expand-collapse {
left: 190px;
}
table.table.data tr.list-tree-level-17 td.list-data-column-1 {
table.table.data tr.list-tree-level-17 td.list-cell-index-1 {
padding-left: 195px;
}
table.table.data tr.list-tree-level-18 a.list-expand-collapse {
left: 200px;
}
table.table.data tr.list-tree-level-18 td.list-data-column-1 {
table.table.data tr.list-tree-level-18 td.list-cell-index-1 {
padding-left: 205px;
}
table.table.data tr.list-tree-level-19 a.list-expand-collapse {
left: 210px;
}
table.table.data tr.list-tree-level-19 td.list-data-column-1 {
table.table.data tr.list-tree-level-19 td.list-cell-index-1 {
padding-left: 215px;
}
table.table.data tr.list-tree-level-20 a.list-expand-collapse {
left: 220px;
}
table.table.data tr.list-tree-level-20 td.list-data-column-1 {
table.table.data tr.list-tree-level-20 td.list-cell-index-1 {
padding-left: 225px;
}
table.table.data tr.list-tree-level-21 a.list-expand-collapse {
left: 230px;
}
table.table.data tr.list-tree-level-21 td.list-data-column-1 {
table.table.data tr.list-tree-level-21 td.list-cell-index-1 {
padding-left: 235px;
}
table.table.data tr.list-tree-level-22 a.list-expand-collapse {
left: 240px;
}
table.table.data tr.list-tree-level-22 td.list-data-column-1 {
table.table.data tr.list-tree-level-22 td.list-cell-index-1 {
padding-left: 245px;
}
table.table.data tr.list-tree-level-23 a.list-expand-collapse {
left: 250px;
}
table.table.data tr.list-tree-level-23 td.list-data-column-1 {
table.table.data tr.list-tree-level-23 td.list-cell-index-1 {
padding-left: 255px;
}
table.table.data tr.list-tree-level-24 a.list-expand-collapse {
left: 260px;
}
table.table.data tr.list-tree-level-24 td.list-data-column-1 {
table.table.data tr.list-tree-level-24 td.list-cell-index-1 {
padding-left: 265px;
}
table.table.data tr.list-tree-level-25 a.list-expand-collapse {
left: 270px;
}
table.table.data tr.list-tree-level-25 td.list-data-column-1 {
table.table.data tr.list-tree-level-25 td.list-cell-index-1 {
padding-left: 275px;
}
.list-preview {
@ -9499,6 +9499,13 @@ table.table.data tr.list-tree-level-25 td.list-data-column-1 {
.control-simplelist ul {
padding-left: 15px;
}
.control-simplelist.form-control ul {
margin-bottom: 0;
}
.control-simplelist.form-control li {
padding-top: 5px;
padding-bottom: 5px;
}
.control-simplelist.with-icons ul,
.control-simplelist.with-checkboxes ul,
.control-simplelist.is-selectable ul {

View File

@ -273,7 +273,7 @@ table.table.data {
.makeTreeLevel(@count) when (@count < 26) {
tr.list-tree-level-@{count} {
a.list-expand-collapse { left: 20px + (10 * @count); }
td.list-data-column-1 { padding-left: 25px + (10 * @count); }
td.list-cell-index-1 { padding-left: 25px + (10 * @count); }
}
.makeTreeLevel(@count + 1);
}

View File

@ -33,6 +33,14 @@
ul { padding-left: 15px; }
&.form-control {
ul { margin-bottom: 0; }
li {
padding-top: 5px;
padding-bottom: 5px;
}
}
&.with-icons, &.with-checkboxes, &.is-selectable {
ul {
list-style-type: none;

View File

@ -25,7 +25,7 @@ class FormController extends ControllerBehavior
/**
* @var Backend\Classes\WidgetBase Reference to the widget object.
*/
private $formWidget;
protected $formWidget;
/**
* {@inheritDoc}
@ -47,7 +47,7 @@ class FormController extends ControllerBehavior
/**
* @var array List of prepared models that require saving.
*/
private $modelsToSave = [];
protected $modelsToSave = [];
/**
* Behavior constructor
@ -349,7 +349,7 @@ class FormController extends ControllerBehavior
* @param array $extras Any extra params to include in the language string variables
* @return string The translated string.
*/
private function getLang($name, $default = null, $extras = [])
protected function getLang($name, $default = null, $extras = [])
{
$name = $this->getConfig($name, $default);
$vars = [
@ -567,7 +567,7 @@ class FormController extends ControllerBehavior
// Internals
//
private function prepareModelsToSave($model, $saveData)
protected function prepareModelsToSave($model, $saveData)
{
$this->modelsToSave = [];
$this->setModelAttributes($model, $saveData);
@ -580,7 +580,7 @@ class FormController extends ControllerBehavior
* @param Model $model Model to save to
* @return array The collection of models to save.
*/
private function setModelAttributes($model, $saveData)
protected function setModelAttributes($model, $saveData)
{
$this->modelsToSave[] = $model;

View File

@ -19,22 +19,22 @@ class ListController extends ControllerBehavior
/**
* @var array List definitions, keys for alias and value for configuration.
*/
private $listDefinitions;
protected $listDefinitions;
/**
* @var string The primary list alias to use. Default: list
*/
private $primaryDefinition;
protected $primaryDefinition;
/**
* @var Backend\Classes\WidgetBase Reference to the list widget object.
*/
private $listWidgets = [];
protected $listWidgets = [];
/**
* @var WidgetBase Reference to the toolbar widget objects.
*/
private $toolbarWidgets = [];
protected $toolbarWidgets = [];
/**
* {@inheritDoc}

View File

@ -26,22 +26,22 @@ class RelationController extends ControllerBehavior
/**
* @var Backend\Classes\WidgetBase Reference to the toolbar widget object.
*/
private $toolbarWidget;
protected $toolbarWidget;
/**
* @var Backend\Classes\WidgetBase Reference to the widget used for viewing (list or form).
*/
private $viewWidget;
protected $viewWidget;
/**
* @var Backend\Classes\WidgetBase Reference to the widget used for relation management.
*/
private $manageWidget;
protected $manageWidget;
/**
* @var Backend\Classes\WidgetBase Reference to widget for relations with pivot data.
*/
private $pivotWidget;
protected $pivotWidget;
/**
* {@inheritDoc}
@ -61,12 +61,12 @@ class RelationController extends ControllerBehavior
/**
* @var array Original configuration values
*/
private $originalConfig;
protected $originalConfig;
/**
* @var bool Has the behavior been initialized.
*/
private $initialized = false;
protected $initialized = false;
/**
* @var string Relationship type
@ -154,6 +154,9 @@ class RelationController extends ControllerBehavior
*/
public function initRelation($model, $field = null)
{
if ($field == null)
$field = post(self::PARAM_FIELD);
$this->config = $this->originalConfig;
$this->model = $model;
$this->field = $field;
@ -323,7 +326,7 @@ class RelationController extends ControllerBehavior
* @param string $field The relationship field.
* @return string The active field name.
*/
private function validateField($field = null)
protected function validateField($field = null)
{
$field = $field ?: post(self::PARAM_FIELD);
@ -403,7 +406,7 @@ class RelationController extends ControllerBehavior
/**
* Returns the existing record IDs for the relation.
*/
private function findExistingRelationIds($checkIds = null)
protected function findExistingRelationIds($checkIds = null)
{
$results = $this->relationObject
->getBaseQuery()

View File

@ -85,7 +85,7 @@ class UserPreferencesModel extends SettingsModel
* Checks if a key is legitimate or should be added to
* the field value collection
*/
private function isKeyAllowed($key)
protected function isKeyAllowed($key)
{
/*
* Let the core columns through

View File

@ -37,17 +37,17 @@ class AuthManager extends RainAuthManager
/**
* @var array Cache of registration callbacks.
*/
private $callbacks = [];
protected $callbacks = [];
/**
* @var array List of registered permissions.
*/
private $permissions = [];
protected $permissions = [];
/**
* @var array Cache of registered permissions.
*/
private $permissionCache = false;
protected $permissionCache = false;
/**
* Registers a callback function that defines authentication permissions.

View File

@ -75,7 +75,7 @@ class BackendController extends ControllerBase
* @param string $action Specifies a method name to execute.
* @return ControllerBase Returns the backend controller object
*/
private function findController($controller, $action, $dirPrefix = null)
protected function findController($controller, $action, $dirPrefix = null)
{
/*
* Workaround: Composer does not support case insensitivity.

View File

@ -53,12 +53,12 @@ class FormField
public $options;
/**
* @var string Specifies a side. Possible values: auto, left, right, full
* @var string Specifies a side. Possible values: auto, left, right, full.
*/
public $span = 'full';
/**
* @var string Specifies a size. Possible values: tiny, small, large, huge, giant
* @var string Specifies a size. Possible values: tiny, small, large, huge, giant.
*/
public $size = 'large';
@ -88,12 +88,12 @@ class FormField
public $comment;
/**
* @var string Specifies the comment position
* @var string Specifies the comment position.
*/
public $commentPosition = 'below';
/**
* @var string Specifies if the comment is in HTML format
* @var string Specifies if the comment is in HTML format.
*/
public $commentHtml = false;

View File

@ -51,15 +51,25 @@ class ListColumn
public $relation;
/**
* @var string Specify a CSS class to attach to the list row element.
* @var string Specify a CSS class to attach to the list cell element.
*/
public $cssClass;
/**
* @var string Specify a format or style for the column value, such as a Date
* @var string Specify a format or style for the column value, such as a Date.
*/
public $format;
/**
* @var string Specifies a path for partial-type fields.
*/
public $path;
/**
* @var array Raw field configuration.
*/
public $config;
/**
* Constructor
*/
@ -75,19 +85,30 @@ class ListColumn
* - number - numeric column, aligned right
* @param string $type Specifies a render mode as described above
*/
public function displayAs($type)
public function displayAs($type, $config)
{
$this->type = $type;
$this->type = strtolower($type) ?: $this->type;
$this->config = $this->evalConfig($config);
return $this;
}
/**
* Specifies CSS classes to apply to the table row element.
* Process options and apply them to this object.
* @param array $config
* @return array
*/
public function cssClass($class)
protected function evalConfig($config)
{
$this->cssClass = $class;
return $this;
if (isset($config['cssClass'])) $this->cssClass = $config['cssClass'];
if (isset($config['searchable'])) $this->searchable = $config['searchable'];
if (isset($config['sortable'])) $this->sortable = $config['sortable'];
if (isset($config['invisible'])) $this->invisible = $config['invisible'];
if (isset($config['select'])) $this->sqlSelect = $config['select'];
if (isset($config['relation'])) $this->relation = $config['relation'];
if (isset($config['format'])) $this->format = $config['format'];
if (isset($config['path'])) $this->path = $config['path'];
return $config;
}
}

View File

@ -17,18 +17,18 @@ class NavigationManager
/**
* @var array Cache of registration callbacks.
*/
private $callbacks = [];
protected $callbacks = [];
/**
* @var array List of registered items.
*/
private $items;
protected $items;
private $contextSidenavPartials = [];
protected $contextSidenavPartials = [];
private $contextOwner;
private $contextMainMenuItemCode;
private $contextSideMenuItemCode;
protected $contextOwner;
protected $contextMainMenuItemCode;
protected $contextSideMenuItemCode;
static $mainItemDefaults = [
'code' => null,
@ -419,7 +419,7 @@ class NavigationManager
* @param array $items A collection of menu items
* @return array The filtered menu items
*/
private function filterItemPermissions($user, array $items)
protected function filterItemPermissions($user, array $items)
{
$items = array_filter($items, function($item) use ($user) {
if (!$item->permissions || !count($item->permissions))
@ -436,7 +436,7 @@ class NavigationManager
* @param object $item
* @return string
*/
private function makeItemKey($owner, $code)
protected function makeItemKey($owner, $code)
{
return strtoupper($owner).'.'.strtoupper($code);
}

View File

@ -27,7 +27,7 @@ class WidgetManager
/**
* @var array Cache of report widget registration callbacks.
*/
private $formWidgetCallbacks = [];
protected $formWidgetCallbacks = [];
/**
* @var array An array of report widgets.
@ -42,7 +42,7 @@ class WidgetManager
/**
* @var array Cache of report widget registration callbacks.
*/
private $reportWidgetCallbacks = [];
protected $reportWidgetCallbacks = [];
/**
* @var array An array where keys are aliases and values are class names.

View File

@ -0,0 +1,41 @@
<?php namespace Backend\Controllers;
use Str;
use Lang;
use File;
use Flash;
use Backend;
use Redirect;
use BackendMenu;
use Backend\Classes\Controller;
use System\Classes\ApplicationException;
use System\Classes\SettingsManager;
use Exception;
/**
* Access Logs controller
*
* @package october\system
* @author Alexey Bobkov, Samuel Georges
*
*/
class AccessLogs extends Controller
{
public $implement = [
'Backend.Behaviors.ListController'
];
public $requiredPermissions = ['system.access_access_logs'];
public $listConfig = 'config_list.yaml';
public function __construct()
{
parent::__construct();
BackendMenu::setContext('October.System', 'system', 'settings');
SettingsManager::setContext('October.Backend', 'access_logs');
}
}

View File

@ -7,6 +7,7 @@ use Redirect;
use Validator;
use BackendAuth;
use Backend\Models\User;
use Backend\Models\AccessLog;
use Backend\Classes\Controller;
use System\Classes\VersionManager;
use System\Classes\ApplicationException;
@ -73,6 +74,9 @@ class Auth extends Controller
'password' => post('password')
], true);
// Log the sign in event
AccessLog::add($user);
// Load version updates
VersionManager::instance()->updateAll();

View File

@ -0,0 +1,4 @@
<p>
<?= e(trans('backend::lang.access_log.hint', ['days' => 60])) ?>
</p>

View File

@ -0,0 +1,3 @@
<div data-control="toolbar">
</div>

View File

@ -0,0 +1,14 @@
# ===================================
# List Behavior Config
# ===================================
title: backend::lang.access_log.menu_label
list: @/modules/backend/models/accesslog/columns.yaml
modelClass: Backend\Models\AccessLog
noRecordsMessage: backend::lang.list.no_records
showSetup: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt

View File

@ -0,0 +1,5 @@
<div class="padded-container list-header">
<?= $this->makeHintPartial('backend_accesslogs_hint', 'hint') ?>
</div>
<?= $this->listRender() ?>

View File

@ -52,10 +52,10 @@
<?php Block::endPut() ?>
<?php else: ?>
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<div class="padded-container">
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<p><a href="<?= Backend::url('backend/users') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.return')) ?></a></p>
</div>

View File

@ -42,10 +42,10 @@
<?php Block::endPut() ?>
<?php else: ?>
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<div class="padded-container">
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<p><a href="<?= Backend::url('backend/users') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.return')) ?></a></p>
</div>

View File

@ -60,10 +60,10 @@
<?php Block::endPut() ?>
<?php else: ?>
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<div class="padded-container">
<div class="control-breadcrumb">
<?= Block::placeholder('breadcrumb') ?>
</div>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<p><a href="<?= Backend::url('backend/users') ?>" class="btn btn-default"><?= e(trans('backend::lang.user.return')) ?></a></p>
</div>

View File

@ -0,0 +1,26 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class DbBackendAccessLog extends Migration
{
public function up()
{
Schema::create('backend_access_log', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('user_id')->unsigned();
$table->string('ip_address')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('backend_access_log');
}
}

View File

@ -38,6 +38,14 @@ class DataGrid extends FormWidgetBase
$this->grid->bindToController();
}
/**
* @return Backend\Widgets\Grid The grid to be displayed.
*/
public function getGrid()
{
return $this->grid;
}
/**
* {@inheritDoc}
*/
@ -74,19 +82,57 @@ class DataGrid extends FormWidgetBase
$grid = new Grid($this->controller, $config);
$grid->alias = $this->alias . 'Grid';
$grid->bindEvent('grid.autocomplete', [$this, 'getAutocompleteValues']);
$grid->bindEvent('grid.dataSource', [$this, 'getDataSourceValues']);
return $grid;
}
/**
* Looks at the model for getXXXAutocompleteValues or getGridAutocompleteValues methods
* to obtain values for autocomplete field types.
* @param string $field Grid field name
* @param string $value Current value
* @param string $data Data for the entire grid
* @return array
*/
public function getAutocompleteValues($field, $value, $data)
{
if (!$this->model->methodExists('getGridAutocompleteValues'))
$methodName = 'get'.studly_case($this->columnName).'AutocompleteValues';
if (!$this->model->methodExists($methodName) && !$this->model->methodExists('getGridAutocompleteValues'))
throw new ApplicationException('Model :model does not contain a method getGridAutocompleteValues()');
$result = $this->model->getGridAutocompleteValues($field, $value, $data);
if ($this->model->methodExists($methodName))
$result = $this->model->$methodName($field, $value, $data);
else
$result = $this->model->getGridAutocompleteValues($this->columnName, $field, $value, $data);
if (!is_array($result))
$result = [];
return $result;
}
}
/**
* Looks at the model for getXXXDataSourceValues or getGridDataSourceValues methods
* to obtain the starting values for the grid.
* @return array
*/
public function getDataSourceValues()
{
$methodName = 'get'.studly_case($this->columnName).'DataSourceValues';
if (!$this->model->methodExists($methodName) && !$this->model->methodExists('getGridDataSourceValues'))
throw new ApplicationException('Model :model does not contain a method getGridDataSourceValues()');
if ($this->model->methodExists($methodName))
$result = $this->model->$methodName();
else
$result = $this->model->getGridDataSourceValues($this->columnName);
if (!is_array($result))
$result = [];
return $result;
}
}

View File

@ -68,10 +68,13 @@ class FileUpload extends FormWidgetBase
$columnName = $this->columnName;
$list = $this->model->$columnName()->withDeferred($this->sessionKey)->orderBy('sort_order')->get();
// Set the thumb for each file
/*
* Set the thumb for each file
*/
foreach ($list as $file) {
$file->thumb = $file->getThumb($this->imageWidth, $this->imageHeight, ['mode' => 'crop']);
}
return $list;
}

View File

@ -1,9 +1,11 @@
<?php namespace Backend\FormWidgets;
use Lang;
use Backend\Classes\FormWidgetBase;
use System\Classes\SystemException;
/**
* Date picker
* Record Finder
* Renders a record finder field.
*
* user:

View File

@ -187,4 +187,15 @@ return [
'locale' => 'Language',
'locale_comment' => 'Select your desired locale for language use.',
],
'access_log' => [
'hint' => 'This log displays a list of successful sign in attempts by administrators. Records are kept for a total of :days days.',
'menu_label' => 'Access Log',
'menu_description' => 'View a list of successful back-end user sign ins.',
'created_at' => 'Date & Time',
'login' => 'Login',
'ip_address' => 'IP address',
'first_name' => 'First name',
'last_name' => 'Last name',
'email' => 'Email',
],
];

View File

@ -1,14 +1,13 @@
<div class="layout responsive-sidebar">
<div class="layout-cell">
<!-- Breadcrumb -->
<?php if ($breadcrumbContent = Block::placeholder('breadcrumb')): ?>
<div class="control-breadcrumb">
<?= $breadcrumbContent ?>
</div>
<?php endif ?>
<div class="padded-container">
<!-- Breadcrumb -->
<?php if ($breadcrumbContent = Block::placeholder('breadcrumb')): ?>
<div class="control-breadcrumb">
<?= $breadcrumbContent ?>
</div>
<?php endif ?>
<?= Block::placeholder('form-contents') ?>
</div>
</div>

View File

@ -0,0 +1,38 @@
<?php namespace Backend\Models;
use Model;
use Request;
/**
* Model for logging access to the back-end
*/
class AccessLog extends Model
{
/**
* @var string The database table used by the model.
*/
protected $table = 'backend_access_log';
/**
* @var array Relations
*/
public $belongsTo = [
'user' => ['Backend\Models\User']
];
/**
* Creates a log record
* @param Backend\Models\User $user Admin user
* @return self
*/
public static function add($user)
{
$record = new static;
$record->user = $user;
$record->ip_address = Request::getClientIp();
$record->save();
return $record;
}
}

View File

@ -0,0 +1,36 @@
# ===================================
# Column Definitions
# ===================================
columns:
created_at:
label: backend::lang.access_log.created_at
searchable: yes
login:
label: backend::lang.access_log.login
relation: user
select: @login
searchable: yes
ip_address:
label: backend::lang.access_log.ip_address
searchable: yes
first_name:
label: backend::lang.access_log.first_name
relation: user
select: @first_name
searchable: yes
last_name:
label: backend::lang.access_log.last_name
relation: user
select: @first_name
searchable: yes
email:
label: backend::lang.access_log.email
relation: user
select: @email
searchable: yes

View File

@ -45,8 +45,8 @@ trait ViewMaker
* Render a partial file contents located in the views folder.
* @param string $partial The view to load.
* @param array $params Parameter variables to pass to the view.
* @param bool $throwException Throw an exception if the partial is not found
* @return string The view contents.
* @param bool $throwException Throw an exception if the partial is not found.
* @return mixed Partial contents or false if not throwing an exception.
*/
public function makePartial($partial, $params = [], $throwException = true)
{

View File

@ -39,14 +39,14 @@ class Form extends WidgetBase
/**
* @var boolean Determines if field definitions have been created.
*/
private $fieldsDefined = false;
protected $fieldsDefined = false;
/**
* @var array Collection of all fields used in this form.
*/
public $allFields = [];
/**
/**
* @var array Collection of all form widgets used in this form.
*/
public $formWidgets = [];
@ -218,7 +218,7 @@ class Form extends WidgetBase
/**
* Prepares the list data
*/
public function prepareVars()
protected function prepareVars()
{
$this->defineFormFields();
$this->vars['sessionKey'] = $this->getSessionKey();
@ -517,7 +517,7 @@ class Form extends WidgetBase
* @param string $fieldType
* @return boolean
*/
private function isFormWidget($fieldType)
protected function isFormWidget($fieldType)
{
if ($fieldType === null)
return false;
@ -539,7 +539,7 @@ class Form extends WidgetBase
/**
* Makes a widget object from a form field object.
*/
public function makeFormWidget($field)
protected function makeFormWidget($field)
{
if ($field->type != 'widget')
return null;
@ -687,7 +687,7 @@ class Form extends WidgetBase
/**
* Looks at the model for defined options.
*/
private function getOptionsFromModel($field, $fieldOptions)
protected function getOptionsFromModel($field, $fieldOptions)
{
/*
* Advanced usage, supplied options are callable
@ -751,7 +751,7 @@ class Form extends WidgetBase
* @param string $method
* @return boolean
*/
private function methodExists($object, $method)
protected function methodExists($object, $method)
{
if (method_exists($object, 'methodExists'))
return $object->methodExists($method);

View File

@ -226,7 +226,7 @@ class Grid extends WidgetBase
case 'currency':
$item['type'] = 'numeric';
$item['format'] = '$0,0.00';
$item['format'] = isset($column['format']) ? $column['format'] : '$0,0.00';
break;
case 'checkbox':
@ -258,4 +258,4 @@ class Grid extends WidgetBase
$this->addJs('js/datagrid.js', 'core');
}
}
}

View File

@ -249,7 +249,7 @@ class Lists extends WidgetBase
* @param string $table
* @return string
*/
private function parseTableName($sql, $table)
protected function parseTableName($sql, $table)
{
return str_replace('@', $table.'.', $sql);
}
@ -502,18 +502,10 @@ class Lists extends WidgetBase
else
$label = studly_case($name);
$column = new ListColumn($name, $label);
$columnType = isset($config['type']) ? $config['type'] : null;
/*
* Process options
*/
if (isset($config['type'])) $column->type = $config['type'];
if (isset($config['searchable'])) $column->searchable = $config['searchable'];
if (isset($config['sortable'])) $column->sortable = $config['sortable'];
if (isset($config['invisible'])) $column->invisible = $config['invisible'];
if (isset($config['select'])) $column->sqlSelect = $config['select'];
if (isset($config['relation'])) $column->relation = $config['relation'];
if (isset($config['format'])) $column->format = $config['format'];
$column = new ListColumn($name, $label);
$column->displayAs($columnType, $config);
return $column;
}
@ -606,6 +598,14 @@ class Lists extends WidgetBase
// Value processing
//
/**
* Process as boolean switch
*/
public function evalPartialTypeValue($value, $column)
{
return $this->controller->makePartial($column->path ?: $column->columnName, ['value' => $value, 'column' => $column]);
}
/**
* Process as boolean switch
*/
@ -679,7 +679,7 @@ class Lists extends WidgetBase
/**
* Validates a column type as a date
*/
private function validateDateTimeValue($value, $column)
protected function validateDateTimeValue($value, $column)
{
if ($value instanceof DateTime)
$value = Carbon::instance($value);

View File

@ -19,7 +19,7 @@ class Toolbar extends WidgetBase
/**
* @var WidgetBase Reference to the search widget object.
*/
private $searchWidget;
protected $searchWidget;
/**
* @var string Name of partial containing control panel.

View File

@ -73,6 +73,12 @@
if (this.options.data) {
handsontableOptions.data = this.options.data
}
/*
* Data from an AJAX data source
*/
else if (this.options.sourceHandler) {
self.refreshDataSource()
}
/*
* Data from a data locker
*/
@ -96,12 +102,6 @@
delete handsontableOptions.data
}
}
/*
* Data from an AJAX data source
*/
else if (this.options.sourceHandler) {
self.refreshDataSource()
}
/*
* Monitor for data changes
@ -454,4 +454,4 @@
Handsontable.PluginHooks.add('afterUpdateSettings', function () {
init.call(this)
});
})(Handsontable, jQuery);
})(Handsontable, jQuery);

View File

@ -19,7 +19,7 @@
<?php $index = 0; foreach ($columns as $key => $column): ?>
<?php $index++; ?>
<td data-title="<?= e(trans($column->label)) ?>" class="list-data-column-<?= $index ?> list-data-column-<?= $column->columnName ?>">
<td data-title="<?= e(trans($column->label)) ?>" class="list-cell-index-<?= $index ?> list-cell-name-<?= $column->columnName ?> list-cell-type-<?= $column->type ?> <?= $column->cssClass ?>">
<?php if ($index == 1 && ($url = $this->getRecordUrl($record))): ?>
<a <?= $this->getRecordOnClick($record) ?> href="<?= $url ?>">
<?= $this->getColumnValue($record, $column) ?>

View File

@ -16,7 +16,7 @@
<?php foreach ($columns as $key => $column): ?>
<?php if ($showSorting && $column->sortable): ?>
<th class="<?= $this->sortColumn==$column->columnName?'sort-'.$this->sortDirection.' active':'sort-desc' ?> list-data-column-<?= $column->columnName ?>"">
<th class="<?= $this->sortColumn==$column->columnName?'sort-'.$this->sortDirection.' active':'sort-desc' ?> list-cell-name-<?= $column->columnName ?>">
<a
href="javascript:;"
data-request="<?= $this->getEventHandler('onSort') ?>"
@ -26,7 +26,7 @@
</a>
</th>
<?php else: ?>
<th class="list-data-column-<?= $column->columnName ?>">
<th class="list-cell-name-<?= $column->columnName ?>">
<span><?= $this->getHeaderValue($column) ?></span>
</th>
<?php endif ?>

View File

@ -20,7 +20,7 @@ class CmsException extends ApplicationException
/**
* @var Cms\Classes\CmsCompoundObject A reference to a CMS object used for masking errors.
*/
private $compoundObject;
protected $compoundObject;
/**
* @var array Collection of error codes for each error distinction.

View File

@ -8,10 +8,10 @@ use Validator;
use System\Classes\SystemException;
use System\Classes\ApplicationException;
use October\Rain\Support\ValidationException;
use Exception;
use RecursiveIteratorIterator;
use RecursiveDirectoryIterator;
use RecursiveIteratorIterator;
use ArrayAccess;
use Exception;
/**
* This is a base class for all CMS objects - content files, pages, partials and layouts.

View File

@ -17,22 +17,22 @@ class CodeParser
/**
* @var \Cms\Classes\CmsCompoundObject A reference to the CMS object being parsed.
*/
private $object;
protected $object;
/**
* @var string Contains a path to the CMS object's file being parsed.
*/
private $filePath;
protected $filePath;
/**
* @var mixed The internal cache, keeps parsed object information during a request.
*/
static private $cache = [];
static protected $cache = [];
/**
* @var string Key for the parsed PHP file information cache.
*/
private $dataCacheKey = 'cms-php-file-data';
protected $dataCacheKey = 'cms-php-file-data';
/**
* Creates the class instance

View File

@ -18,7 +18,7 @@ class ComponentManager
/**
* @var array Cache of registration callbacks.
*/
private $callbacks = [];
protected $callbacks = [];
/**
* @var array An array where keys are codes and values are class names.

View File

@ -16,9 +16,10 @@ use Twig_Environment;
use Controller as BaseController;
use Cms\Twig\Loader as TwigLoader;
use Cms\Twig\Extension as CmsTwigExtension;
use System\Twig\Extension as SystemTwigExtension;
use Cms\Classes\FileHelper as CmsFileHelper;
use System\Models\RequestLog;
use System\Classes\ErrorHandler;
use System\Twig\Extension as SystemTwigExtension;
use October\Rain\Support\Markdown;
use October\Rain\Support\ValidationException;
use Illuminate\Http\RedirectResponse;
@ -141,6 +142,9 @@ class Controller extends BaseController
if (!$page) {
$this->setStatusCode(404);
// Log the 404 request
RequestLog::add();
if (!$page = $this->router->findByUrl('/404'))
return Response::make(View::make('cms::404'), $this->statusCode);
}
@ -535,8 +539,12 @@ class Controller extends BaseController
/**
* Renders a requested partial.
* The framework uses this method internally.
* @param string $partial The view to load.
* @param array $params Parameter variables to pass to the view.
* @param bool $throwException Throw an exception if the partial is not found.
* @return mixed Partial contents or false if not throwing an exception.
*/
public function renderPartial($name, $parameters = [])
public function renderPartial($name, $parameters = [], $throwException = true)
{
/*
* Alias @ symbol for ::
@ -555,18 +563,26 @@ class Controller extends BaseController
* Component alias not supplied
*/
if (!strlen($componentAlias)) {
if ($this->componentContext !== null)
if ($this->componentContext !== null) {
$componentObj = $this->componentContext;
elseif (($componentObj = $this->findComponentByPartial($partialName)) === null)
throw new CmsException(Lang::get('cms::lang.partial.not_found', ['name'=>$name]));
}
elseif (($componentObj = $this->findComponentByPartial($partialName)) === null) {
if ($throwException)
throw new CmsException(Lang::get('cms::lang.partial.not_found', ['name'=>$name]));
else
return false;
}
}
/*
* Component alias is supplied
*/
else {
if (($componentObj = $this->findComponentByName($componentAlias)) === null)
throw new CmsException(Lang::get('cms::lang.component.not_found', ['name'=>$componentAlias]));
if (($componentObj = $this->findComponentByName($componentAlias)) === null) {
if ($throwException)
throw new CmsException(Lang::get('cms::lang.component.not_found', ['name'=>$componentAlias]));
else
return false;
}
}
$partial = null;
@ -587,8 +603,12 @@ class Controller extends BaseController
$partial = ComponentPartial::loadCached($componentObj, $partialName);
if ($partial === null)
throw new CmsException(Lang::get('cms::lang.partial.not_found', ['name'=>$name]));
if ($partial === null) {
if ($throwException)
throw new CmsException(Lang::get('cms::lang.partial.not_found', ['name'=>$name]));
else
return false;
}
/*
* Set context for self access
@ -599,8 +619,12 @@ class Controller extends BaseController
/*
* Process theme partial
*/
if (($partial = Partial::loadCached($this->theme, $name)) === null)
throw new CmsException(Lang::get('cms::lang.partial.not_found', ['name'=>$name]));
if (($partial = Partial::loadCached($this->theme, $name)) === null) {
if ($throwException)
throw new CmsException(Lang::get('cms::lang.partial.not_found', ['name'=>$name]));
else
return false;
}
}
CmsException::mask($partial, 400);
@ -665,7 +689,7 @@ class Controller extends BaseController
return $result;
}
return $this->renderPartial($name.'::default');
return $this->renderPartial($name.'::default', [], false);
}
/**
@ -797,7 +821,7 @@ class Controller extends BaseController
* Searches the layout and page components by an alias
* @return ComponentBase The component object, if found
*/
private function findComponentByName($name)
protected function findComponentByName($name)
{
if (isset($this->page->components[$name]))
return $this->page->components[$name];
@ -812,7 +836,7 @@ class Controller extends BaseController
* Searches the layout and page components by an AJAX handler
* @return ComponentBase The component object, if found
*/
private function findComponentByHandler($handler)
protected function findComponentByHandler($handler)
{
foreach ($this->page->components as $component) {
if (method_exists($component, $handler))
@ -831,7 +855,7 @@ class Controller extends BaseController
* Searches the layout and page components by a partial file
* @return ComponentBase The component object, if found
*/
private function findComponentByPartial($partial)
protected function findComponentByPartial($partial)
{
foreach ($this->page->components as $component) {
$fileName = ComponentPartial::getFilePath($component, $partial);

View File

@ -42,7 +42,7 @@ class Router
/**
* @var array A list of parameters names and values extracted from the URL pattern and URL string.
*/
private $parameters = [];
protected $parameters = [];
/**
* @var array Contains the URL map - the list of page file names and corresponding URL patterns.

View File

@ -6,6 +6,7 @@ use Lang;
use Cache;
use Event;
use Config;
use DbDongle;
use October\Rain\Support\Yaml;
use System\Models\Parameters;
use System\Classes\SystemException;
@ -97,13 +98,15 @@ class Theme
$paramKey = 'cms::theme.active';
$activeTheme = Config::get('cms.activeTheme');
$dbResult = Parameters::findRecord($paramKey)
->remember(1440, $paramKey)
->pluck('value')
;
if (DbDongle::hasDatabase()) {
$dbResult = Parameters::findRecord($paramKey)
->remember(1440, $paramKey)
->pluck('value')
;
if ($dbResult !== null)
$activeTheme = $dbResult;
if ($dbResult !== null)
$activeTheme = $dbResult;
}
$apiResult = Event::fire('cms.activeTheme', [], true);
if ($apiResult !== null)

View File

@ -394,7 +394,7 @@ class Index extends Controller
* @param string $markup The markup to convert to unix style endings
* @return string
*/
private function convertLineEndings($markup)
protected function convertLineEndings($markup)
{
$markup = str_replace("\r\n", "\n", $markup);
$markup = str_replace("\r", "\n", $markup);

View File

@ -22,7 +22,7 @@ class Extension extends Twig_Extension
/**
* @var \Cms\Classes\Controller A reference to the CMS controller.
*/
private $controller;
protected $controller;
/**
* Creates the extension instance.

View File

@ -5,6 +5,7 @@ use Lang;
use Event;
use Config;
use Backend;
use DbDongle;
use BackendMenu;
use BackendAuth;
use Twig_Environment;
@ -16,6 +17,7 @@ use System\Classes\SettingsManager;
use System\Twig\Engine as TwigEngine;
use System\Twig\Loader as TwigLoader;
use System\Twig\Extension as TwigExtension;
use System\Models\EventLog;
use System\Models\MailSettings;
use System\Models\MailTemplate;
use Backend\Classes\WidgetManager;
@ -73,6 +75,16 @@ class ServiceProvider extends ModuleServiceProvider
return $handler->handleException($exception, $httpCode, $isConsole);
});
/*
* Write all log events to the database
*/
Event::listen('illuminate.log', function($level, $message, $context){
if (!DbDongle::hasDatabase())
return;
EventLog::add($message, $level);
});
/*
* Register basic Twig
*/
@ -99,7 +111,7 @@ class ServiceProvider extends ModuleServiceProvider
});
/*
* Override system email with email settings
* Override system mailer with mail settings
*/
Event::listen('mailer.beforeRegister', function() {
if (MailSettings::isConfigured())
@ -202,7 +214,7 @@ class ServiceProvider extends ModuleServiceProvider
*/
SettingsManager::instance()->registerCallback(function($manager){
$manager->registerSettingItems('October.System', [
'email_settings' => [
'mail_settings' => [
'label' => 'system::lang.mail.menu_label',
'description' => 'system::lang.mail.menu_description',
'category' => 'System',
@ -235,8 +247,25 @@ class ServiceProvider extends ModuleServiceProvider
'url' => Backend::url('system/updates'),
'permissions' => ['system.manage_updates'],
'order' => 700
]
],
'event_logs' => [
'label' => 'system::lang.event_log.menu_label',
'description' => 'system::lang.event_log.menu_description',
'category' => 'Logs',
'icon' => 'icon-exclamation-triangle',
'url' => Backend::url('system/eventlogs'),
'permissions' => ['system.access_event_logs'],
'order' => 800
],
'request_logs' => [
'label' => 'system::lang.request_log.menu_label',
'description' => 'system::lang.request_log.menu_description',
'category' => 'Logs',
'icon' => 'icon-file-o',
'url' => Backend::url('system/requestlogs'),
'permissions' => ['system.access_request_logs'],
'order' => 800
],
]);
});
@ -261,7 +290,6 @@ class ServiceProvider extends ModuleServiceProvider
/*
* Register the sidebar for the System main menu
*/
BackendMenu::registerContextSidenavPartial('October.System', 'system', '@/modules/system/partials/_system_sidebar.htm');
}

View File

@ -176,7 +176,7 @@ class SettingsModel extends ModelBehavior
* Checks if a key is legitimate or should be added to
* the field value collection
*/
private function isKeyAllowed($key)
protected function isKeyAllowed($key)
{
/*
* Let the core columns through

View File

@ -18,7 +18,7 @@ class ExceptionBase extends Exception
/**
* @var Exception If this exception is acting as a mask, this property stores the face exception.
*/
private $mask;
protected $mask;
/**
* @var string Hint Message to help the user with troubleshooting the error (optional).
@ -43,7 +43,7 @@ class ExceptionBase extends Exception
/**
* @var stdObject Cached code information for highlighting code.
*/
private $highlight;
protected $highlight;
/**
* CMS base exception class constructor. Inherits the native PHP Exception.
@ -254,7 +254,7 @@ class ExceptionBase extends Exception
* @param array $traceInfo The trace information from getTrace() or debug_backtrace().
* @return array The filtered array containing the trace information.
*/
private function filterCallStack($traceInfo)
protected function filterCallStack($traceInfo)
{
/*
* Determine if filter should be used at all.

View File

@ -24,7 +24,7 @@ class MarkupManager
/**
* @var array Cache of registration callbacks.
*/
private $callbacks = [];
protected $callbacks = [];
/**
* @var array Registered extension items

View File

@ -17,20 +17,20 @@ class SettingsManager
/**
* @var array Cache of registration callbacks.
*/
private $callbacks = [];
protected $callbacks = [];
/**
* @var array List of registered items.
*/
private $items;
protected $items;
/**
* @var array Flat collection of all items.
*/
private $allItems;
protected $allItems;
private $contextOwner;
private $contextItemCode;
protected $contextOwner;
protected $contextItemCode;
static $itemDefaults = [
'code' => null,
@ -270,7 +270,7 @@ class SettingsManager
* @param array $items A collection of setting items
* @return array The filtered settings items
*/
private function filterItemPermissions($user, array $items)
protected function filterItemPermissions($user, array $items)
{
array_filter($items, function($item) use ($user) {
if (!$item->permissions || !count($item->permissions))

View File

@ -606,7 +606,7 @@ class UpdateManager
* @param string $fileCode A unique file code
* @return string Full path on the disk
*/
private function getFilePath($fileCode)
protected function getFilePath($fileCode)
{
$name = md5($fileCode) . '.arc';
return $this->tempDirectory . '/' . $name;
@ -628,7 +628,7 @@ class UpdateManager
* @param string $uri URI
* @return string URL
*/
private function createServerUrl($uri)
protected function createServerUrl($uri)
{
$gateway = Config::get('cms.updateServer', 'http://octobercms.com/api');
if (substr($gateway, -1) != '/')
@ -643,7 +643,7 @@ class UpdateManager
* @param array $postData Post data
* @return void
*/
private function applyHttpAttributes($http, $postData)
protected function applyHttpAttributes($http, $postData)
{
$postData['url'] = base64_encode(URL::to('/'));
@ -664,7 +664,7 @@ class UpdateManager
* Create a nonce based on millisecond time
* @return int
*/
private function createNonce()
protected function createNonce()
{
$mt = explode(' ', microtime());
return $mt[1] . substr($mt[0], 2, 6);
@ -674,7 +674,7 @@ class UpdateManager
* Create a unique signature for transmission.
* @return string
*/
private function createSignature($data, $secret)
protected function createSignature($data, $secret)
{
return base64_encode(hash_hmac('sha512', http_build_query($data, '', '&'), base64_decode($secret), true));
}

View File

@ -243,7 +243,7 @@ class VersionManager
/**
* Returns the absolute path to a version file for a plugin.
*/
private function getVersionFile($code)
protected function getVersionFile($code)
{
$versionFile = $this->pluginManager->getPluginPath($code) . '/updates/version.yaml';
return $versionFile;
@ -252,7 +252,7 @@ class VersionManager
/**
* Checks if a plugin has a version file.
*/
private function hasVersionFile($code)
protected function hasVersionFile($code)
{
$versionFile = $this->getVersionFile($code);
return File::isFile($versionFile);

View File

@ -0,0 +1,52 @@
<?php namespace System\Controllers;
use Str;
use Lang;
use File;
use Flash;
use Backend;
use Redirect;
use BackendMenu;
use Backend\Classes\Controller;
use System\Classes\ApplicationException;
use System\Classes\SettingsManager;
use System\Models\EventLog;
use Exception;
/**
* Event Logs controller
*
* @package october\system
* @author Alexey Bobkov, Samuel Georges
*
*/
class EventLogs extends Controller
{
public $implement = [
'Backend.Behaviors.FormController',
'Backend.Behaviors.ListController'
];
public $requiredPermissions = ['system.access_event_logs'];
public $formConfig = 'config_form.yaml';
public $listConfig = 'config_list.yaml';
public function __construct()
{
parent::__construct();
BackendMenu::setContext('October.System', 'system', 'settings');
SettingsManager::setContext('October.System', 'event_logs');
}
public function onEmptyLog()
{
EventLog::truncate();
Flash::success(Lang::get('system::lang.event_log.empty_success'));
return $this->listRefresh();
}
}

View File

@ -0,0 +1,52 @@
<?php namespace System\Controllers;
use Str;
use Lang;
use File;
use Flash;
use Backend;
use Redirect;
use BackendMenu;
use Backend\Classes\Controller;
use System\Classes\ApplicationException;
use System\Classes\SettingsManager;
use System\Models\RequestLog;
use Exception;
/**
* Request Logs controller
*
* @package october\system
* @author Alexey Bobkov, Samuel Georges
*
*/
class RequestLogs extends Controller
{
public $implement = [
'Backend.Behaviors.FormController',
'Backend.Behaviors.ListController'
];
public $requiredPermissions = ['system.access_request_logs'];
public $formConfig = 'config_form.yaml';
public $listConfig = 'config_list.yaml';
public function __construct()
{
parent::__construct();
BackendMenu::setContext('October.System', 'system', 'settings');
SettingsManager::setContext('October.System', 'request_logs');
}
public function onEmptyLog()
{
RequestLog::truncate();
Flash::success(Lang::get('system::lang.event_log.empty_success'));
return $this->listRefresh();
}
}

View File

@ -23,7 +23,7 @@ class Settings extends Controller
/**
* @var WidgetBase Reference to the widget object.
*/
private $formWidget;
protected $formWidget;
public $requiredPermissions = ['system.manage_settings'];
@ -144,7 +144,7 @@ class Settings extends Controller
/**
* Locates a setting item for a module or plugin
*/
private function findSettingItem($author, $plugin, $code)
protected function findSettingItem($author, $plugin, $code)
{
$manager = SettingsManager::instance();

View File

@ -263,7 +263,7 @@ class Updates extends Controller
return $this->makePartial('execute');
}
private function buildUpdateSteps($core, $plugins, $themes)
protected function buildUpdateSteps($core, $plugins, $themes)
{
if (!is_array($core))
$core = [null, null];

View File

@ -0,0 +1,4 @@
<p>
<?= e(trans('system::lang.event_log.hint')) ?>
</p>

View File

@ -0,0 +1,9 @@
<div data-control="toolbar" class="loading-indicator-container">
<a
href="javascript:;"
data-request="onEmptyLog"
data-load-indicator="<?= e(trans('system::lang.event_log.empty_loading')) ?>"
class="btn btn-default oc-icon-trash-o">
<?= e(trans('system::lang.event_log.empty_link')) ?>
</a>
</div>

View File

@ -0,0 +1 @@
<?= Str::limit($value, 100) ?>

View File

@ -0,0 +1,19 @@
# ===================================
# Form Behavior Config
# ===================================
# Record name
name: Log
# Model Form Field configuration
form: @/modules/system/models/eventlog/fields.yaml
# Model Class name
modelClass: System\Models\EventLog
# Default redirect location
defaultRedirect: system/eventlogs
# Preview page
preview:
title: Event

View File

@ -0,0 +1,15 @@
# ===================================
# List Behavior Config
# ===================================
title: system::lang.event_log.menu_label
list: @/modules/system/models/eventlog/columns.yaml
modelClass: System\Models\EventLog
recordUrl: system/eventlogs/preview/:id
noRecordsMessage: backend::lang.list.no_records
showSetup: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt

View File

@ -0,0 +1,5 @@
<div class="padded-container list-header">
<?= $this->makeHintPartial('system_eventlogs_hint', 'hint') ?>
</div>
<?= $this->listRender() ?>

View File

@ -0,0 +1,41 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('system/eventlogs') ?>"><?= e(trans('system::lang.event_log.menu_label')) ?></a></li>
<li><?= e($this->pageTitle) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<div class="scoreboard">
<div data-control="toolbar">
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.event_log.id_label')) ?></h4>
<p>#<?= $formModel->id ?></p>
</div>
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.event_log.level')) ?></h4>
<p><?= $formModel->level ?></p>
</div>
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.event_log.created_at')) ?></h4>
<p><?= $formModel->created_at->toDayDateTimeString() ?></p>
</div>
</div>
</div>
<div class="layout-item stretch layout-column">
<?= $this->formRenderPreview() ?>
</div>
<?php else: ?>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<?php endif ?>
<p>
<a href="<?= Backend::url('system/eventlogs') ?>" class="btn btn-default oc-icon-chevron-left">
<?= e(trans('system::lang.event_log.return_link')) ?>
</a>
</p>

View File

@ -0,0 +1,4 @@
<p>
<?= e(trans('system::lang.request_log.hint')) ?>
</p>

View File

@ -0,0 +1,9 @@
<div data-control="toolbar" class="loading-indicator-container">
<a
href="javascript:;"
data-request="onEmptyLog"
data-load-indicator="<?= e(trans('system::lang.request_log.empty_loading')) ?>"
class="btn btn-default oc-icon-trash-o">
<?= e(trans('system::lang.request_log.empty_link')) ?>
</a>
</div>

View File

@ -0,0 +1,11 @@
<?php if (count($formModel->referer) > 0): ?>
<div class="form-control control-simplelist with-icons">
<ul>
<?php foreach ((array) $formModel->referer as $referer): ?>
<li class="oc-icon-file-o"><?= $referer ?></li>
<?php endforeach ?>
</ul>
</div>
<?php else: ?>
<div class="form-control"><em>There were no detected referers to this URL.</em></div>
<?php endif ?>

View File

@ -0,0 +1,19 @@
# ===================================
# Form Behavior Config
# ===================================
# Record name
name: Log
# Model Form Field configuration
form: @/modules/system/models/requestlog/fields.yaml
# Model Class name
modelClass: System\Models\RequestLog
# Default redirect location
defaultRedirect: system/requestlogs
# Preview page
preview:
title: Request

View File

@ -0,0 +1,15 @@
# ===================================
# List Behavior Config
# ===================================
title: system::lang.request_log.menu_label
list: @/modules/system/models/requestlog/columns.yaml
modelClass: System\Models\RequestLog
recordUrl: system/requestlogs/preview/:id
noRecordsMessage: backend::lang.list.no_records
showSetup: true
toolbar:
buttons: list_toolbar
search:
prompt: backend::lang.list.search_prompt

View File

@ -0,0 +1,5 @@
<div class="padded-container list-header">
<?= $this->makeHintPartial('system_requestlogs_hint', 'hint') ?>
</div>
<?= $this->listRender() ?>

View File

@ -0,0 +1,45 @@
<?php Block::put('breadcrumb') ?>
<ul>
<li><a href="<?= Backend::url('system/requestlogs') ?>"><?= e(trans('system::lang.request_log.menu_label')) ?></a></li>
<li><?= e($this->pageTitle) ?></li>
</ul>
<?php Block::endPut() ?>
<?php if (!$this->fatalError): ?>
<div class="scoreboard">
<div data-control="toolbar">
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.request_log.id_label')) ?></h4>
<p>#<?= $formModel->id ?></p>
</div>
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.request_log.status_code')) ?></h4>
<p><?= $formModel->status_code ?></p>
</div>
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.request_log.count')) ?></h4>
<p><?= $formModel->count ?></p>
</div>
<div class="scoreboard-item title-value">
<h4><?= e(trans('system::lang.request_log.referer')) ?></h4>
<p><?= $formModel->referer ? count($formModel->referer) : 0 ?></p>
</div>
</div>
</div>
<div class="layout-item stretch layout-column">
<?= $this->formRenderPreview() ?>
</div>
<?php else: ?>
<p class="flash-message static error"><?= e($this->fatalError) ?></p>
<?php endif ?>
<p>
<a href="<?= Backend::url('system/requestlogs') ?>" class="btn btn-default oc-icon-chevron-left">
<?= e(trans('system::lang.request_log.return_link')) ?>
</a>
</p>

View File

@ -0,0 +1,27 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class DbSystemEventLogs extends Migration
{
public function up()
{
Schema::create('system_event_logs', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->string('level')->nullable()->index();
$table->text('message')->nullable();
$table->mediumtext('details')->nullable();
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('system_event_logs');
}
}

View File

@ -0,0 +1,28 @@
<?php
use Illuminate\Database\Schema\Blueprint;
use Illuminate\Database\Migrations\Migration;
class DbSystemRequestLogs extends Migration
{
public function up()
{
Schema::create('system_request_logs', function(Blueprint $table)
{
$table->engine = 'InnoDB';
$table->increments('id');
$table->integer('status_code')->nullable();
$table->string('url')->nullable();
$table->text('referer')->nullable();
$table->integer('count')->default(0);
$table->timestamps();
});
}
public function down()
{
Schema::dropIfExists('system_request_logs');
}
}

View File

@ -176,4 +176,33 @@ return [
'zip' => [
'extract_failed' => "Unable to extract core file ':file'.",
],
'event_log' => [
'hint' => 'This log displays a list of potential errors that occur in the application, such as exceptions and debugging information.',
'menu_label' => 'Event Log',
'menu_description' => 'View system log messages with their recorded time and details.',
'empty_link' => 'Empty event log',
'empty_loading' => 'Emptying event log...',
'empty_success' => 'Successfully emptied the event log.',
'return_link' => 'Return to event log',
'id' => 'ID',
'id_label' => 'Event ID',
'created_at' => 'Date & Time',
'message' => 'Message',
'level' => 'Level',
],
'request_log' => [
'hint' => 'This log displays a list of browser requests that may require attention. For example, if a visitor opens a CMS page that cannot be found, a record is created with the status code 404.',
'menu_label' => 'Request Log',
'menu_description' => 'View bad or redirected requests, such as Page not found (404).',
'empty_link' => 'Empty request log',
'empty_loading' => 'Emptying request log...',
'empty_success' => 'Successfully emptied the request log.',
'return_link' => 'Return to request log',
'id' => 'ID',
'id_label' => 'Log ID',
'count' => 'Counter',
'referer' => 'Referers',
'url' => 'URL',
'status_code' => 'Status',
],
];

View File

@ -0,0 +1,52 @@
<?php namespace System\Models;
use Model;
/**
* Model for logging system errors and debug trace messages
*/
class EventLog extends Model
{
/**
* @var string The database table used by the model.
*/
protected $table = 'system_event_logs';
/**
* @var array List of attribute names which are json encoded and decoded from the database.
*/
protected $jsonable = ['details'];
/**
* Creates a log record
* @param string $message Specifies the message text
* @param string $level Specifies the logging level
* @param string $details Specifies the error details string
* @return self
*/
public static function add($message, $level = 'info', $details = null)
{
$record = new static;
$record->message = $message;
$record->level = $level;
if ($details !== null)
$record->details = (array) $details;
$record->save();
return $record;
}
/**
* Beautify level value.
* @param string $level
* @return string
*/
public function getLevelAttribute($level)
{
return ucfirst($level);
}
}

View File

@ -0,0 +1,55 @@
<?php namespace System\Models;
use Model;
use Request;
/**
* Model for logging 404 errors
*/
class RequestLog extends Model
{
/**
* @var string The database table used by the model.
*/
protected $table = 'system_request_logs';
/**
* @var array The attributes that aren't mass assignable.
*/
protected $guarded = [];
/**
* @var array List of attribute names which are json encoded and decoded from the database.
*/
protected $jsonable = ['referer'];
/**
* Creates a log record
* @return self
*/
public static function add($statusCode = 404)
{
$record = static::firstOrNew([
'url' => Request::fullUrl(),
'status_code' => $statusCode,
]);
if ($referer = Request::header('referer')) {
$referers = (array) $record->referer ?: [];
$referers[] = $referer;
$record->referer = $referers;
}
if (!$record->exists) {
$record->count = 1;
$record->save();
}
else {
$record->increment('count');
}
return $record;
}
}

View File

@ -0,0 +1,18 @@
# ===================================
# Column Definitions
# ===================================
columns:
id:
label: system::lang.event_log.id
searchable: yes
created_at:
label: system::lang.event_log.created_at
searchable: yes
message:
label: system::lang.event_log.message
searchable: yes
type: partial
path: message_column

View File

@ -0,0 +1,8 @@
# ===================================
# Field Definitions
# ===================================
fields:
message:
type: textarea

View File

@ -0,0 +1,15 @@
# ===================================
# Column Definitions
# ===================================
columns:
status_code:
label: system::lang.request_log.status_code
searchable: yes
url:
label: system::lang.request_log.url
searchable: yes
count:
label: system::lang.request_log.count

View File

@ -0,0 +1,13 @@
# ===================================
# Field Definitions
# ===================================
fields:
url:
label: system::lang.request_log.url
referer:
label: system::lang.request_log.referer
type: partial
path: referer_field

View File

@ -212,7 +212,7 @@ trait AssetMaker
* @param array $asset Stored asset array
* @return string
*/
private function getAssetEntryBuildPath($asset)
protected function getAssetEntryBuildPath($asset)
{
$path = $asset['path'];
if (isset($asset['attributes']['build'])) {
@ -234,7 +234,7 @@ trait AssetMaker
* @param string $asset Specifies a path (URL) to the asset.
* @return string
*/
private function getAssetScheme($asset)
protected function getAssetScheme($asset)
{
if (preg_match("/(\/\/|http|https)/", $asset))
return $asset;

View File

@ -20,7 +20,7 @@ class Extension extends Twig_Extension
/**
* @var \System\Classes\MarkupManager A reference to the markup manager instance.
*/
private $markupManager;
protected $markupManager;
/**
* Creates the extension instance.

View File

@ -1,7 +1,7 @@
<?php
use Cms\Classes\Controller;
use Cms\Classes\Theme;
use Cms\Classes\Controller;
class ControllerTest extends TestCase
{
@ -10,7 +10,6 @@ class ControllerTest extends TestCase
/*
* Test the built-in 404 page
*/
$theme = new Theme();
$theme->load('apitest');
$controller = new Controller($theme);
@ -26,7 +25,6 @@ class ControllerTest extends TestCase
/*
* Test the theme 404 page
*/
$theme = new Theme();
$theme->load('test');
$controller = new Controller($theme);