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

This commit is contained in:
alekseybobkov 2015-01-21 21:39:03 -08:00
commit e7e50bc684
20 changed files with 343 additions and 136 deletions

View File

@ -1,3 +1,6 @@
* **Build 18x** (2015-01-xx)
- Relation Controller now supports *has one* and *belongs to* relations (see Backend > Relations docs).
* **Build 179** (2015-01-18)
- The Settings page is no longer governed by a global permission, each settings entry should have its own permission definition.

View File

@ -259,13 +259,16 @@ class RelationController extends ControllerBehavior
protected function evalManageMode()
{
switch ($this->relationType) {
case 'belongsTo':
$mode = 'list';
break;
case 'belongsToMany':
$mode = (isset($this->config->pivot)) ? 'pivot' : 'list';
break;
case 'hasOne':
case 'hasMany':
case 'belongsTo':
$mode = 'form';
break;
}
@ -317,10 +320,16 @@ class RelationController extends ControllerBehavior
* @param string $field Relation definition.
* @return array The relation element selector as the key, and the relation view contents are the value.
*/
public function relationRefresh($field)
public function relationRefresh($field = null)
{
$field = $this->validateField($field);
return ['#'.$this->relationGetId('view') => $this->relationRenderView()];
$result = ['#'.$this->relationGetId('view') => $this->relationRenderView()];
if ($toolbar = $this->relationRenderToolbar()) {
$result['#'.$this->relationGetId('toolbar')] = $toolbar;
}
return $result;
}
/**
@ -375,7 +384,9 @@ class RelationController extends ControllerBehavior
$this->vars['relationType'] = $this->relationType;
$this->vars['relationSearchWidget'] = $this->searchWidget;
$this->vars['relationToolbarWidget'] = $this->toolbarWidget;
$this->vars['relationManageMode'] = $this->manageMode;
$this->vars['relationManageWidget'] = $this->manageWidget;
$this->vars['relationViewMode'] = $this->viewMode;
$this->vars['relationViewWidget'] = $this->viewWidget;
$this->vars['relationPivotWidget'] = $this->pivotWidget;
$this->vars['relationSessionKey'] = $this->relationGetSessionKey();
@ -454,18 +465,56 @@ class RelationController extends ControllerBehavior
//
// Overrides
//
/**
* Controller override: Extend the query used for populating the list
* after the default query is processed.
* @param October\Rain\Database\Builder $query
* @param string $field
* @param string $manageMode
*/
public function relationExtendQuery($query, $field, $manageMode)
public function relationExtendQuery($query, $field)
{
}
//
// AJAX (Buttons)
//
public function onRelationButtonAdd()
{
return $this->onRelationManageForm();
}
public function onRelationButtonCreate()
{
return $this->onRelationManageForm();
}
public function onRelationButtonDelete()
{
return $this->onRelationManageDelete();
}
public function onRelationButtonLink()
{
return $this->onRelationManageForm();
}
public function onRelationButtonUnlink()
{
return $this->onRelationManageRemove();
}
public function onRelationButtonRemove()
{
return $this->onRelationManageRemove();
}
public function onRelationButtonUpdate()
{
return $this->onRelationManageForm();
}
//
// AJAX
//
@ -491,12 +540,18 @@ class RelationController extends ControllerBehavior
public function onRelationManageCreate()
{
$this->beforeAjax();
$saveData = $this->manageWidget->getSaveData();
$newModel = $this->relationObject->create($saveData, $this->relationGetSessionKey(true));
$newModel->commitDeferred($this->manageWidget->getSessionKey());
return ['#'.$this->relationGetId('view') => $this->relationRenderView()];
if ($this->viewMode == 'multi') {
$newModel = $this->relationObject->create($saveData, $this->relationGetSessionKey(true));
$newModel->commitDeferred($this->manageWidget->getSessionKey());
}
elseif ($this->viewMode == 'single') {
$this->viewWidget->setFormValues($saveData);
$this->viewWidget->model->save();
}
return $this->relationRefresh();
}
/**
@ -505,9 +560,16 @@ class RelationController extends ControllerBehavior
public function onRelationManageUpdate()
{
$this->beforeAjax();
$saveData = $this->manageWidget->getSaveData();
$this->relationObject->find($this->manageId)->save($saveData, $this->manageWidget->getSessionKey());
if ($this->viewMode == 'multi') {
$model = $this->relationObject->find($this->manageId);
$model->save($saveData, $this->manageWidget->getSessionKey());
}
elseif ($this->viewMode == 'single') {
$this->viewWidget->setFormValues($saveData);
$this->viewWidget->model->save();
}
return ['#'.$this->relationGetId('view') => $this->relationRenderView()];
}
@ -519,18 +581,35 @@ class RelationController extends ControllerBehavior
{
$this->beforeAjax();
if (($checkedIds = post('checked')) && is_array($checkedIds)) {
$relatedModel = $this->relationObject->getRelated();
foreach ($checkedIds as $relationId) {
if (!$obj = $relatedModel->find($relationId)) {
continue;
}
/*
* Multiple (has many, belongs to many)
*/
if ($this->viewMode == 'multi') {
if (($checkedIds = post('checked')) && is_array($checkedIds)) {
$relatedModel = $this->relationObject->getRelated();
foreach ($checkedIds as $relationId) {
if (!$obj = $relatedModel->find($relationId)) {
continue;
}
$obj->delete();
$obj->delete();
}
}
}
/*
* Single (belongs to, has one)
*/
elseif ($this->viewMode == 'single') {
$relatedModel = $this->viewWidget->model;
if ($relatedModel->exists) {
$relatedModel->delete();
}
return ['#'.$this->relationGetId('view') => $this->relationRenderView()];
$this->viewWidget->setFormValues([]);
$this->viewWidget->model = $this->relationModel;
}
return $this->relationRefresh();
}
/**
@ -540,31 +619,48 @@ class RelationController extends ControllerBehavior
{
$this->beforeAjax();
if ($this->viewMode != 'multi') {
throw new ApplicationException(Lang::get('backend::lang.relation.invalid_action_single'));
$recordId = post('record_id');
/*
* Add
*/
if ($this->viewMode == 'multi') {
$checkedIds = $recordId ? [$recordId] : post('checked');
if (is_array($checkedIds)) {
/*
* Remove existing relations from the array
*/
$existingIds = $this->findExistingRelationIds($checkedIds);
$checkedIds = array_diff($checkedIds, $existingIds);
$foreignKeyName = $this->relationModel->getKeyName();
$models = $this->relationModel->whereIn($foreignKeyName, $checkedIds)->get();
foreach ($models as $model) {
if ($this->model->exists) {
$this->relationObject->add($model);
}
else {
$this->relationObject->add($model, $this->relationGetSessionKey());
}
}
}
}
if (($checkedIds = post('checked')) && is_array($checkedIds)) {
/*
* Remove existing relations from the array
*/
$existingIds = $this->findExistingRelationIds($checkedIds);
$checkedIds = array_diff($checkedIds, $existingIds);
$foreignKeyName = $this->relationModel->getKeyName();
$models = $this->relationModel->whereIn($foreignKeyName, $checkedIds)->get();
foreach ($models as $model) {
if ($this->model->exists) {
$this->relationObject->add($model);
}
else {
$this->relationObject->add($model, $this->relationGetSessionKey());
}
/*
* Link
*/
elseif ($this->viewMode == 'single') {
if ($recordId && ($model = $this->relationModel->find($recordId))) {
$this->relationObject->associate($model);
$this->relationObject->getParent()->save();
$this->viewWidget->setFormValues($model->attributes);
}
}
return ['#'.$this->relationGetId('view') => $this->relationRenderView()];
return $this->relationRefresh();
}
/**
@ -574,15 +670,29 @@ class RelationController extends ControllerBehavior
{
$this->beforeAjax();
if ($this->viewMode != 'multi') {
throw new ApplicationException(Lang::get('backend::lang.relation.invalid_action_single'));
$recordId = post('record_id');
/*
* Remove
*/
if ($this->viewMode == 'multi') {
$checkedIds = $recordId ? [$recordId] : post('checked');
if (is_array($checkedIds)) {
$this->relationObject->detach($checkedIds);
}
}
/*
* Unlink
*/
elseif ($this->viewMode == 'single') {
$this->relationObject->dissociate();
$this->relationObject->getParent()->save();
$this->viewWidget->setFormValues([]);
}
if (($checkedIds = post('checked')) && is_array($checkedIds)) {
$this->relationObject->detach($checkedIds);
}
return ['#'.$this->relationGetId('view') => $this->relationRenderView()];
return $this->relationRefresh();
}
public function onRelationManagePivotForm()
@ -698,7 +808,7 @@ class RelationController extends ControllerBehavior
$config->showCheckboxes = $this->getConfig('view[showCheckboxes]', !$this->readOnly);
$defaultOnClick = sprintf(
"$.oc.relationBehavior.clickManageListRecord(:id, '%s', '%s')",
"$.oc.relationBehavior.clickViewListRecord(:id, '%s', '%s')",
$this->field,
$this->relationGetSessionKey()
);
@ -715,7 +825,7 @@ class RelationController extends ControllerBehavior
*/
$widget = $this->makeWidget('Backend\Widgets\Lists', $config);
$widget->bindEvent('list.extendQuery', function ($query) {
$this->controller->relationExtendQuery($query, $this->field, $this->manageMode);
$this->controller->relationExtendQuery($query, $this->field);
$this->relationObject->setQuery($query);
if ($this->model->exists) {
@ -744,13 +854,18 @@ class RelationController extends ControllerBehavior
* Single (belongs to, has one)
*/
elseif ($this->viewMode == 'single') {
$query = $this->relationObject;
$this->controller->relationExtendQuery($query, $this->field);
$model = $query->getResults() ?: $this->relationModel;
$config = $this->makeConfig($this->config->form);
$config->model = $this->relationModel;
$config->model = $model;
$config->arrayName = class_basename($this->relationModel);
$config->context = 'relation';
$config->alias = $this->alias . 'ViewForm';
$widget = $this->makeWidget('Backend\Widgets\Form', $config);
$widget->previewMode = true;
}
return $widget;
@ -786,6 +901,16 @@ class RelationController extends ControllerBehavior
$config->showSorting = $this->getConfig('manage[showSorting]', true);
$config->defaultSort = $this->getConfig('manage[defaultSort]');
$config->recordsPerPage = $this->getConfig('manage[recordsPerPage]');
if ($this->viewMode == 'single') {
$config->showCheckboxes = false;
$config->recordOnClick = sprintf(
"$.oc.relationBehavior.clickManageListRecord(:id, '%s', '%s')",
$this->field,
$this->relationGetSessionKey()
);
}
$widget = $this->makeWidget('Backend\Widgets\Lists', $config);
/*
@ -836,7 +961,7 @@ class RelationController extends ControllerBehavior
*/
if ($this->manageMode == 'pivot' || $this->manageMode == 'list') {
$widget->bindEvent('list.extendQuery', function ($query) {
$this->controller->relationExtendQuery($query, $this->field, $this->manageMode);
$this->controller->relationExtendQuery($query, $this->field);
/*
* Where not in the current list of related records

View File

@ -11,6 +11,9 @@
.relation-behavior .control-list:last-child > table {
margin-bottom: 0;
}
.relation-behavior.relation-view-single {
padding: 0 20px;
}
.relation-flush .relation-behavior {
border-top: 0;
}

View File

@ -6,6 +6,20 @@
var RelationBehavior = function() {
this.clickManageListRecord = function(recordId, relationField, sessionKey) {
var oldPopup = $('#relationManagePopup')
$.request('onRelationManageAdd', {
data: {
'record_id': recordId,
'_relation_field': relationField,
'_session_key': sessionKey
}
})
oldPopup.popup('hide')
}
this.clickViewListRecord = function(recordId, relationField, sessionKey) {
var newPopup = $('<a />')
newPopup.popup({

View File

@ -17,6 +17,10 @@
.control-list:last-child > table {
margin-bottom: 0;
}
&.relation-view-single {
padding: 0 20px;
}
}
// Relation manager to sit flush to the element above

View File

@ -1,6 +1,6 @@
<a
data-control="popup"
data-handler="onRelationManageForm"
data-handler="onRelationButtonAdd"
href="javascript:;"
class="btn btn-sm btn-primary oc-icon-plus">
<?= e(trans('backend::lang.relation.add_name', ['name'=>trans($relationLabel)])) ?>

View File

@ -1,6 +1,6 @@
<a
data-control="popup"
data-handler="onRelationManageForm"
data-handler="onRelationButtonCreate"
href="javascript:;"
class="btn btn-sm btn-primary oc-icon-plus">
<?= e(trans('backend::lang.relation.create_name', ['name'=>trans($relationLabel)])) ?>

View File

@ -1,14 +1,24 @@
<button
class="btn btn-sm btn-default oc-icon-trash-o"
onclick="$(this).data('request-data', {
checked: $('#<?= $this->relationGetId('view') ?> .control-list').listWidget('getChecked')
})"
disabled="disabled"
data-request="onRelationManageDelete"
data-request-confirm="<?= e(trans('backend::lang.relation.delete_confirm')) ?>"
data-trigger-type="enable"
data-trigger="#<?= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"
data-stripe-load-indicator>
<?= e(trans('backend::lang.relation.delete')) ?>
</button>
<?php if ($relationViewMode == 'single'): ?>
<button
class="btn btn-sm btn-default oc-icon-trash-o"
data-request="onRelationButtonDelete"
data-request-confirm="<?= e(trans('backend::lang.relation.delete_confirm')) ?>"
data-stripe-load-indicator>
<?= e(trans('backend::lang.relation.delete')) ?>
</button>
<?php else: ?>
<button
class="btn btn-sm btn-default oc-icon-trash-o"
onclick="$(this).data('request-data', {
checked: $('#<?= $this->relationGetId('view') ?> .control-list').listWidget('getChecked')
})"
disabled="disabled"
data-request="onRelationButtonDelete"
data-request-confirm="<?= e(trans('backend::lang.relation.delete_confirm')) ?>"
data-trigger-type="enable"
data-trigger="#<?= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"
data-stripe-load-indicator>
<?= e(trans('backend::lang.relation.delete')) ?>
</button>
<?php endif ?>

View File

@ -0,0 +1,7 @@
<a
data-control="popup"
data-handler="onRelationButtonLink"
href="javascript:;"
class="btn btn-sm btn-primary oc-icon-link">
<?= e(trans('backend::lang.relation.link_name', ['name'=>trans($relationLabel)])) ?>
</a>

View File

@ -4,7 +4,7 @@
checked: $('#<?= $this->relationGetId('view') ?> .control-list').listWidget('getChecked')
})"
disabled="disabled"
data-request="onRelationManageRemove"
data-request="onRelationButtonRemove"
data-trigger-type="enable"
data-trigger="#<?= $this->relationGetId('view') ?> .control-list input[type=checkbox]"
data-trigger-condition="checked"

View File

@ -0,0 +1,8 @@
<a
href="javascript:;"
class="btn btn-sm btn-default oc-icon-unlink"
data-request="onRelationButtonUnlink"
data-request-confirm="<?= e(trans('backend::lang.relation.unlink_confirm')) ?>"
data-stripe-load-indicator>
<?= e(trans('backend::lang.relation.unlink_name', ['name'=>trans($relationLabel)])) ?>
</a>

View File

@ -0,0 +1,8 @@
<a
data-control="popup"
data-handler="onRelationButtonUpdate"
data-request-data="manage_id: '<?= $relationManageId ?>'"
href="javascript:;"
class="btn btn-sm btn-primary oc-icon-pencil">
<?= e(trans('backend::lang.relation.update_name', ['name'=>trans($relationLabel)])) ?>
</a>

View File

@ -1,6 +1,6 @@
<div
data-request-data="_relation_field: '<?= $relationField ?>'" id="<?= $this->relationGetId() ?>"
class="relation-behavior">
class="relation-behavior relation-view-<?= $relationViewMode ?>">
<?php if ($toolbar = $this->relationRenderToolbar()): ?>
<!-- Relation Toolbar -->

View File

@ -1,5 +1,5 @@
<?= Form::open() ?>
<div data-request-data="_relation_field: '<?= $relationField ?>'">
<div id="relationManagePopup" data-request-data="_relation_field: '<?= $relationField ?>'">
<?= Form::open() ?>
<div class="modal-header">
<button type="button" class="close" data-dismiss="popup">&times;</button>
<h4 class="modal-title"><?= e(trans('backend::lang.relation.add_a_new', ['name'=>$relationLabel])) ?></h4>
@ -13,14 +13,16 @@
</div>
<div class="modal-footer">
<button
type="button"
class="btn btn-primary"
data-request="onRelationManageAdd"
data-dismiss="popup"
data-stripe-load-indicator>
<?= e(trans('backend::lang.relation.add_selected')) ?>
</button>
<?php if ($relationManageWidget->showCheckboxes): ?>
<button
type="button"
class="btn btn-primary"
data-request="onRelationManageAdd"
data-dismiss="popup"
data-stripe-load-indicator>
<?= e(trans('backend::lang.relation.add_selected')) ?>
</button>
<?php endif ?>
<button
type="button"
class="btn btn-default"
@ -28,5 +30,5 @@
<?= e(trans('backend::lang.relation.cancel')) ?>
</button>
</div>
</div>
<?= Form::close() ?>
<?= Form::close() ?>
</div>

View File

@ -10,17 +10,22 @@
<?= $this->relationMakePartial('button_add') ?>
<?= $this->relationMakePartial('button_remove') ?>
<?php elseif ($relationType == 'belongsToTODO'): ?>
<?php elseif ($relationType == 'belongsTo'): ?>
<?= $this->relationMakePartial('button_link') ?>
<?= $this->relationMakePartial('button_unlink') ?>
<?php elseif ($relationType == 'hasOneTODO'): ?>
<?php elseif ($relationType == 'hasOne'): ?>
<?= $this->relationMakePartial('button_create') ?>
<?= $this->relationMakePartial('button_update') ?>
<?= $this->relationMakePartial('button_delete') ?>
<?php if ($relationViewWidget->model->exists): ?>
<?= $this->relationMakePartial('button_update', ['relationManageId' => $relationViewWidget->model->id]) ?>
<?= $this->relationMakePartial('button_delete') ?>
<?php else: ?>
<?= $this->relationMakePartial('button_create') ?>
<?php endif ?>
<?php endif ?>
</div>

View File

@ -95,7 +95,7 @@ class RecordFinder extends FormWidgetBase
if (!$this->model->hasRelation($this->relationName)) {
throw new SystemException(Lang::get('backend::lang.model.missing_relation', [
'class' => get_class($this->controller),
'class' => get_class($this->model),
'relation' => $this->relationName
]));
}

View File

@ -113,9 +113,10 @@ class Relation extends FormWidgetBase
}
elseif (in_array($this->relationType, ['belongsTo', 'hasOne'])) {
$field->type = 'dropdown';
$field->placeholder = $this->emptyOption;
}
$field->placeholder = $this->emptyOption;
// It is safe to assume that if the model and related model are of
// the exact same class, then it cannot be related to itself
if ($model->exists && (get_class($model) == get_class($relatedObj))) {

View File

@ -196,6 +196,8 @@ return [
'add' => "Add",
'add_selected' => "Add selected",
'add_a_new' => "Add a new :name",
'link_selected' => "Link selected",
'link_a_new' => "Link a new :name",
'cancel' => "Cancel",
'close' => "Close",
'add_name' => "Add :name",
@ -210,6 +212,11 @@ return [
'delete' => "Delete",
'delete_name' => "Delete :name",
'delete_confirm' => "Are you sure?",
'link' => "Link",
'link_name' => "Link :name",
'unlink' => "Unlink",
'unlink_name' => "Unlink :name",
'unlink_confirm' => "Are you sure?",
],
'model' => [
'name' => 'Model',

View File

@ -3,61 +3,61 @@
# ===================================
fields:
first_name:
span: left
label: backend::lang.user.first_name
first_name:
span: left
label: backend::lang.user.first_name
last_name:
span: right
label: backend::lang.user.last_name
last_name:
span: right
label: backend::lang.user.last_name
login:
span: left
label: backend::lang.user.login
login:
span: left
label: backend::lang.user.login
email:
span: right
label: backend::lang.user.email
email:
span: right
label: backend::lang.user.email
password:
type: password
span: left
label: backend::lang.user.password
password:
type: password
span: left
label: backend::lang.user.password
password_confirmation:
type: password
span: right
label: backend::lang.user.password_confirmation
password_confirmation:
type: password
span: right
label: backend::lang.user.password_confirmation
send_invite:
context: create
type: checkbox
label: backend::lang.user.send_invite
comment: backend::lang.user.send_invite_comment
send_invite:
context: create
type: checkbox
label: backend::lang.user.send_invite
comment: backend::lang.user.send_invite_comment
tabs:
fields:
fields:
permissions[superuser]:
context: [create, update]
tab: backend::lang.user.permissions
label: backend::lang.user.superuser
type: checkbox
comment: backend::lang.user.superuser_comment
permissions[superuser]:
context: [create, update]
tab: backend::lang.user.permissions
label: backend::lang.user.superuser
type: checkbox
comment: backend::lang.user.superuser_comment
groups:
context: [create, update]
tab: backend::lang.user.permissions
label: backend::lang.user.groups
commentAbove: backend::lang.user.groups_comment
type: checkboxlist
groups:
context: [create, update]
tab: backend::lang.user.permissions
label: backend::lang.user.groups
commentAbove: backend::lang.user.groups_comment
type: checkboxlist
secondaryTabs:
fields:
fields:
avatar:
label: backend::lang.user.avatar
type: fileupload
mode: image
imageHeight: 260
imageWidth: 260
avatar:
label: backend::lang.user.avatar
type: fileupload
mode: image
imageHeight: 260
imageWidth: 260

View File

@ -329,6 +329,16 @@ class Controller extends BaseController
$result = $template->render($this->vars);
CmsException::unmask();
/*
* Extensibility
*/
if (
($event = $this->fireEvent('page.display', [$url, $page, $result], true)) ||
($event = Event::fire('cms.page.display', [$this, $url, $page, $result], true))
) {
return $event;
}
if (!is_string($result)) {
return $result;
}
@ -412,8 +422,8 @@ class Controller extends BaseController
/*
* Extensibility
*/
Event::fire('cms.page.initComponents', [$this, $this->page]);
$this->fireEvent('page.initComponents', [$this->page, $this->layout]);
Event::fire('cms.page.initComponents', [$this, $this->page, $this->layout]);
}
/**