BUG Fix broken member / group import

BUG GridFieldImportButton no longer only works on ModelAdmin
This commit is contained in:
Damian Mooyman 2017-01-10 17:57:54 +13:00
parent 9e563ebd3a
commit b62f9b60a0
No known key found for this signature in database
GPG Key ID: 78B823A10DE27D1A
9 changed files with 186 additions and 106 deletions

View File

@ -2039,41 +2039,41 @@ this.closest(".grid-field").addClass("show-filter"),this.parent().html('<span cl
var n=this.find(".edit-link")
n.length&&this.getGridField().showDetailView(n.prop("href"))},onmouseover:function c(){this.find(".edit-link").length&&this.css("cursor","pointer")},onmouseout:function d(){this.css("cursor","default")
}}),e(".grid-field .action.action_import:button").entwine({onclick:function f(t){t.preventDefault()
}}),e(".grid-field .action.action_import:button").entwine({onclick:function f(e){e.preventDefault(),this.openmodal()},onmatch:function p(){this._super(),"open"===this.data("state")&&this.openmodal()},onunmatch:function h(){
this._super()},openmodal:function m(){var t=e(this.data("target"))
t.remove(),t=e(this.data("modal")),t.appendTo(document.body)
var n=e(".modal-backdrop")
n.length<1?(n=e('<div class="modal-backdrop fade in"></div>'),e("body").append(n)):n.addClass("fade in").fadeIn()
var i=e(this.data("target"))
i.addClass("in"),i.find("[data-dismiss]").on("click",function(){n.fadeOut(function(){n.removeClass("in")}),i.removeClass("in")})}}),e(".grid-field .action:button").entwine({onclick:function p(e){var t="show"
n.length<1?(n=e('<div class="modal-backdrop fade in"></div>'),e("body").append(n)):n.addClass("fade in").fadeIn(),t.addClass("in"),t.find("[data-dismiss]").on("click",function(){n.fadeOut(function(){n.removeClass("in")
}),t.removeClass("in")})}}),e(".grid-field .action:button").entwine({onclick:function g(e){var t="show"
return this.is(":disabled")?void e.preventDefault():(!this.hasClass("ss-gridfield-button-close")&&this.closest(".grid-field").hasClass("show-filter")||(t="hidden"),this.getGridField().reload({data:[{name:this.attr("name"),
value:this.val(),filter:t}]}),void e.preventDefault())},actionurl:function h(){var t=this.closest(":button"),n=this.getGridField(),i=this.closest("form"),r=i.find(":input.gridstate").serialize(),a=i.find('input[name="SecurityID"]').val()
value:this.val(),filter:t}]}),void e.preventDefault())},actionurl:function v(){var t=this.closest(":button"),n=this.getGridField(),i=this.closest("form"),r=i.find(":input.gridstate").serialize(),a=i.find('input[name="SecurityID"]').val()
r+="&"+encodeURIComponent(t.attr("name"))+"="+encodeURIComponent(t.val()),a&&(r+="&SecurityID="+encodeURIComponent(a)),window.location.search&&(r=window.location.search.replace(/^\?/,"")+"&"+r)
var o=n.data("url").indexOf("?")==-1?"?":"&"
return e.path.makeUrlAbsolute(n.data("url")+o+r,e("base").attr("href"))}}),e(".grid-field .add-existing-autocompleter").entwine({onbuttoncreate:function m(){var e=this
this.toggleDisabled(),this.find('input[type="text"]').on("keyup",function(){e.toggleDisabled()})},onunmatch:function g(){this.find('input[type="text"]').off("keyup")},toggleDisabled:function v(){var e=this.find(".ss-ui-button"),t=this.find('input[type="text"]'),n=""!==t.val(),i=e.is(":disabled")
return e.path.makeUrlAbsolute(n.data("url")+o+r,e("base").attr("href"))}}),e(".grid-field .add-existing-autocompleter").entwine({onbuttoncreate:function y(){var e=this
this.toggleDisabled(),this.find('input[type="text"]').on("keyup",function(){e.toggleDisabled()})},onunmatch:function b(){this.find('input[type="text"]').off("keyup")},toggleDisabled:function _(){var e=this.find(".ss-ui-button"),t=this.find('input[type="text"]'),n=""!==t.val(),i=e.is(":disabled")
;(n&&i||!n&&!i)&&e.attr("disabled",!i)}}),e(".grid-field .grid-field__col-compact .action.gridfield-button-delete, .cms-edit-form .btn-toolbar button.action.action-delete").entwine({onclick:function y(e){
return confirm(s["default"]._t("TABLEFIELD.DELETECONFIRMMESSAGE"))?void this._super(e):(e.preventDefault(),!1)}}),e(".grid-field .action.gridfield-button-print").entwine({UUID:null,onmatch:function b(){
this._super(),this.setUUID((new Date).getTime())},onunmatch:function _(){this._super()},onclick:function w(e){var t=this.actionurl()
return window.open(t),e.preventDefault(),!1}}),e(".ss-gridfield-print-iframe").entwine({onmatch:function C(){this._super(),this.hide().bind("load",function(){this.focus()
;(n&&i||!n&&!i)&&e.attr("disabled",!i)}}),e(".grid-field .grid-field__col-compact .action.gridfield-button-delete, .cms-edit-form .btn-toolbar button.action.action-delete").entwine({onclick:function w(e){
return confirm(s["default"]._t("TABLEFIELD.DELETECONFIRMMESSAGE"))?void this._super(e):(e.preventDefault(),!1)}}),e(".grid-field .action.gridfield-button-print").entwine({UUID:null,onmatch:function C(){
this._super(),this.setUUID((new Date).getTime())},onunmatch:function T(){this._super()},onclick:function E(e){var t=this.actionurl()
return window.open(t),e.preventDefault(),!1}}),e(".ss-gridfield-print-iframe").entwine({onmatch:function P(){this._super(),this.hide().bind("load",function(){this.focus()
var e=this.contentWindow||this
e.print()})},onunmatch:function T(){this._super()}}),e(".grid-field .action.no-ajax").entwine({onclick:function E(e){return window.location.href=this.actionurl(),e.preventDefault(),!1}}),e(".grid-field .action-detail").entwine({
onclick:function P(){return this.getGridField().showDetailView(e(this).prop("href")),!1}}),e(".grid-field[data-selectable]").entwine({getSelectedItems:function O(){return this.find(".ss-gridfield-item.ui-selected")
e.print()})},onunmatch:function O(){this._super()}}),e(".grid-field .action.no-ajax").entwine({onclick:function S(e){return window.location.href=this.actionurl(),e.preventDefault(),!1}}),e(".grid-field .action-detail").entwine({
onclick:function k(){return this.getGridField().showDetailView(e(this).prop("href")),!1}}),e(".grid-field[data-selectable]").entwine({getSelectedItems:function j(){return this.find(".ss-gridfield-item.ui-selected")
},getSelectedIDs:function S(){return e.map(this.getSelectedItems(),function(t){return e(t).data("id")})}}),e(".grid-field[data-selectable] .ss-gridfield-items").entwine({onadd:function k(){this._super(),
this.selectable()},onremove:function j(){this._super(),this.data("selectable")&&this.selectable("destroy")}}),e(".grid-field .filter-header :input").entwine({onmatch:function x(){var e=this.closest(".extra").find(".ss-gridfield-button-filter"),t=this.closest(".extra").find(".ss-gridfield-button-reset")
},getSelectedIDs:function x(){return e.map(this.getSelectedItems(),function(t){return e(t).data("id")})}}),e(".grid-field[data-selectable] .ss-gridfield-items").entwine({onadd:function R(){this._super(),
this.selectable()},onremove:function I(){this._super(),this.data("selectable")&&this.selectable("destroy")}}),e(".grid-field .filter-header :input").entwine({onmatch:function F(){var e=this.closest(".extra").find(".ss-gridfield-button-filter"),t=this.closest(".extra").find(".ss-gridfield-button-reset")
this.val()&&(e.addClass("filtered"),t.addClass("filtered")),this._super()},onunmatch:function R(){this._super()},onkeydown:function I(e){if(!this.closest(".ss-gridfield-button-reset").length){var t=this.closest(".extra").find(".ss-gridfield-button-filter"),n=this.closest(".extra").find(".ss-gridfield-button-reset")
this.val()&&(e.addClass("filtered"),t.addClass("filtered")),this._super()},onunmatch:function A(){this._super()},onkeydown:function D(e){if(!this.closest(".ss-gridfield-button-reset").length){var t=this.closest(".extra").find(".ss-gridfield-button-filter"),n=this.closest(".extra").find(".ss-gridfield-button-reset")
if("13"==e.keyCode){var i=this.closest(".filter-header").find(".ss-gridfield-button-filter"),r="show"
return!this.hasClass("ss-gridfield-button-close")&&this.closest(".grid-field").hasClass("show-filter")||(r="hidden"),this.getGridField().reload({data:[{name:i.attr("name"),value:i.val(),filter:r}]}),!1
}t.addClass("hover-alike"),n.addClass("hover-alike")}}}),e(".grid-field .relation-search").entwine({onfocusin:function F(t){this.autocomplete({source:function n(t,i){var r=e(this.element),a=e(this.element).closest("form")
}t.addClass("hover-alike"),n.addClass("hover-alike")}}}),e(".grid-field .relation-search").entwine({onfocusin:function M(t){this.autocomplete({source:function n(t,i){var r=e(this.element),a=e(this.element).closest("form")
e.ajax({headers:{"X-Pjax":"Partial"},dataType:"json",type:"GET",url:e(r).data("searchUrl"),data:encodeURIComponent(r.attr("name"))+"="+encodeURIComponent(r.val()),success:i,error:function o(e){alert(s["default"]._t("GRIDFIELD.ERRORINTRANSACTION","An error occured while fetching data from the server\n Please try again later."))
@ -2081,7 +2081,7 @@ e.ajax({headers:{"X-Pjax":"Partial"},dataType:"json",type:"GET",url:e(r).data("s
}})},select:function i(t,n){var i=e('<input type="hidden" name="relationID" class="action_gridfield_relationfind" />')
i.val(n.item.id),e(this).closest(".grid-field").find(".action_gridfield_relationfind").replaceWith(i)
var r=e(this).closest(".grid-field").find(".action_gridfield_relationadd")
r.removeAttr("disabled")}})}}),e(".grid-field .pagination-page-number input").entwine({onkeydown:function A(t){if(13==t.keyCode){var n=parseInt(e(this).val(),10),i=e(this).getGridField()
r.removeAttr("disabled")}})}}),e(".grid-field .pagination-page-number input").entwine({onkeydown:function N(t){if(13==t.keyCode){var n=parseInt(e(this).val(),10),i=e(this).getGridField()
return i.setState("GridFieldPaginator",{currentPage:n}),i.reload(),!1}}})})},function(e,t,n){"use strict"
function i(e){if(e&&e.__esModule)return e
var t={}

View File

@ -15594,7 +15594,7 @@ div.grid-field__sort-field+.form__fieldgroup-item{
}
.grid-field-import.in .modal-content{
overflow-y:scroll;
overflow-y:auto;
}
.grid-field-import.in .modal-dialog{

View File

@ -264,7 +264,7 @@ div.grid-field__sort-field + .form__fieldgroup-item {
display: block;
.modal-content {
overflow-y: scroll;
overflow-y: auto;
}
.modal-dialog {

View File

@ -137,9 +137,30 @@ $.entwine('ss', function($) {
$('.grid-field .action.action_import:button').entwine({
onclick: function(e) {
e.preventDefault();
this.openmodal();
},
onmatch: function() {
this._super();
// Trigger auto-open
if (this.data('state') === 'open') {
this.openmodal();
}
},
onunmatch: function() {
this._super();
},
var backdrop = $('.modal-backdrop');
openmodal: function() {
// Remove existing modal
let modal = $(this.data('target'));
modal.remove();
// Add modal to end of body tag
modal = $(this.data('modal'));
modal.appendTo(document.body);
// Apply backdrop
let backdrop = $('.modal-backdrop');
if(backdrop.length < 1) {
backdrop = $('<div class="modal-backdrop fade in"></div>');
$('body').append(backdrop);
@ -147,8 +168,6 @@ $.entwine('ss', function($) {
backdrop.addClass('fade in').fadeIn();
}
var modal = $(this.data('target'));
modal.addClass('in');
modal.find('[data-dismiss]').on('click', function() {
backdrop.fadeOut(function() {

View File

@ -11,6 +11,7 @@ use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\FormAction;
use SilverStripe\Forms\GridField\GridFieldDetailForm;
use SilverStripe\Forms\GridField\GridFieldFilterHeader;
use SilverStripe\Forms\ResetFormAction;
use SilverStripe\Forms\RequiredFields;
use SilverStripe\Forms\HiddenField;
@ -30,8 +31,8 @@ use SilverStripe\ORM\DataObject;
use SilverStripe\ORM\Search\SearchContext;
use SilverStripe\ORM\SS_List;
use SilverStripe\Security\Member;
use SilverStripe\View\Requirements;
use SilverStripe\View\ArrayData;
use SilverStripe\View\SSViewer;
/**
* Generates a three-pane UI for editing model classes, with an
@ -170,7 +171,7 @@ abstract class ModelAdmin extends LeftAndMain
$list,
$fieldConfig = GridFieldConfig_RecordEditor::create($this->stat('page_length'))
->addComponent($exportButton)
->removeComponentsByType('SilverStripe\\Forms\\GridField\\GridFieldFilterHeader')
->removeComponentsByType(GridFieldFilterHeader::class)
->addComponents(new GridFieldPrintButton('buttons-before-left'))
);
@ -178,21 +179,16 @@ abstract class ModelAdmin extends LeftAndMain
if (singleton($this->modelClass)->hasMethod('getCMSValidator')) {
$detailValidator = singleton($this->modelClass)->getCMSValidator();
/** @var GridFieldDetailForm $detailform */
$detailform = $listField->getConfig()->getComponentByType('SilverStripe\\Forms\\GridField\\GridFieldDetailForm');
$detailform = $listField->getConfig()->getComponentByType(GridFieldDetailForm::class);
$detailform->setValidator($detailValidator);
}
if ($this->showImportForm) {
$import = CompositeField::create(array(
new LiteralField(
'ImportForm',
$this->customise(new ArrayData(array(
)))->renderWith('SilverStripe\\Forms\\GridField\\GridFieldImportButton_Modal')
)
));
$fieldConfig->addComponent(new GridFieldImportButton('buttons-before-left', $import));
$fieldConfig->addComponent(
GridFieldImportButton::create('buttons-before-left')
->setImportForm($this->ImportForm())
->setModalTitle(_t('ModelAdmin.IMPORT', 'Import from CSV'))
);
}
$form = Form::create(

View File

@ -11,9 +11,7 @@ use SilverStripe\Forms\LiteralField;
use SilverStripe\Forms\Tab;
use SilverStripe\Forms\TabSet;
use SilverStripe\Forms\HiddenField;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Forms\FieldList;
use SilverStripe\Forms\HeaderField;
use SilverStripe\Forms\Form;
use SilverStripe\Forms\GridField\GridFieldConfig_RecordEditor;
use SilverStripe\Forms\GridField\GridFieldButtonRow;
@ -178,29 +176,16 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider
// Add import capabilities. Limit to admin since the import logic can affect assigned permissions
if (Permission::check('ADMIN')) {
// @todo when grid field is converted to react use the react component
$memberImport = CompositeField::create(array(
new LiteralField(
'MemberImportFormIframe',
$this->customise(new ArrayData(array(
'URL' => $this->Link('memberimport')
)))->renderWith('SilverStripe\\Forms\\GridField\\GridFieldImportButton_Modal')
)
));
$memberListConfig
->addComponent(new GridFieldImportButton('buttons-before-left', $memberImport));
$groupImport = CompositeField::create(array(
new LiteralField(
'GroupImportFormIframe',
$this->customise(new ArrayData(array(
'URL' => $this->Link('groupimport')
)))->renderWith('SilverStripe\\Forms\\GridField\\GridFieldImportButton_Modal')
)
));
$groupListConfig
->addComponent(new GridFieldImportButton('buttons-before-left', $groupImport));
$memberListConfig->addComponent(
GridFieldImportButton::create('buttons-before-left')
->setImportIframe($this->Link('memberimport'))
->setModalTitle(_t('SecurityAdmin.IMPORTUSERS', 'Import users'))
);
$groupListConfig->addComponent(
GridFieldImportButton::create('buttons-before-left')
->setImportIframe($this->Link('groupimport'))
->setModalTitle(_t('SecurityAdmin.IMPORTGROUPS', 'Import groups'))
);
}
// Tab nav in CMS is rendered through separate template
@ -277,11 +262,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider
/** @var Group $group */
$group = $this->currentPage();
/** @skipUpgrade */
$form = new MemberImportForm(
$this,
'MemberImportForm'
);
$form = new MemberImportForm($this, __FUNCTION__);
$form->setGroup($group);
return $form;
@ -312,8 +293,7 @@ class SecurityAdmin extends LeftAndMain implements PermissionProvider
return null;
}
$form = new GroupImportForm($this, 'GroupImportForm');
return $form;
return new GroupImportForm($this, __FUNCTION__);
}
/**

View File

@ -35,20 +35,6 @@
<div class="cms-content-view">
$EditForm
</div>
<% if ImportForm %>
<div id="{$EditForm.HTMLID}_ImportModal" class="modal fade grid-field-import" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content"><div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
$ImportForm
</div>
</div>
</div>
<% end_if %>
</div>
</div>

View File

@ -2,27 +2,46 @@
namespace SilverStripe\Forms\GridField;
use SilverStripe\Forms\CompositeField;
use SilverStripe\Control\Session;
use SilverStripe\Core\Injector\Injectable;
use SilverStripe\Forms\Form;
use SilverStripe\View\ArrayData;
use SilverStripe\View\SSViewer;
class GridFieldImportButton implements GridField_HTMLProvider
{
use Injectable;
/**
* Fragment to write the button to
*/
protected $targetFragment;
/**
* @var CompositeField
* Import form
*
* @var Form
*/
protected $importFormField;
protected $importForm;
/**
* @var string
*/
protected $modalTitle = null;
/**
* URL for iframe
*
* @var string
*/
protected $importIframe = null;
/**
* @param string $targetFragment The HTML fragment to write the button into
*/
public function __construct($targetFragment = "after", $importFormField = null)
public function __construct($targetFragment = "after")
{
$this->targetFragment = $targetFragment;
$this->importFormField = $importFormField;
}
/**
@ -33,6 +52,23 @@ class GridFieldImportButton implements GridField_HTMLProvider
*/
public function getHTMLFragments($gridField)
{
$modalID = $gridField->ID() . '_ImportModal';
// Check for form message prior to rendering form (which clears session messages)
$form = $this->getImportForm();
$hasMessage = $form && $form->getMessage();
// Render modal
$template = SSViewer::get_templates_by_class(static::class, '_Modal');
$viewer = new ArrayData([
'ImportModalTitle' => $this->getModalTitle(),
'ImportModalID' => $modalID,
'ImportIframe' => $this->getImportIframe(),
'ImportForm' => $this->getImportForm(),
]);
$modal = $viewer->renderWith($template)->forTemplate();
// Build action button
$button = new GridField_FormAction(
$gridField,
'import',
@ -40,15 +76,18 @@ class GridFieldImportButton implements GridField_HTMLProvider
'import',
null
);
$button->addExtraClass('btn btn-secondary no-ajax font-icon-upload btn--icon-large action_import');
// means that you can only have 1 import per page
$button
->setAttribute('data-toggle', "modal")
->setAttribute('data-target', "#". $gridField->getForm()->getHTMLID() . '_ImportModal');
->addExtraClass('btn btn-secondary no-ajax font-icon-upload btn--icon-large action_import')
->setForm($gridField->getForm())
->setAttribute('data-toggle', 'modal')
->setAttribute('aria-controls', $modalID)
->setAttribute('data-target', "#{$modalID}")
->setAttribute('data-modal', $modal);
$button->setForm($gridField->getForm());
$extra = null;
// If form has a message, trigger it to automatically open
if ($hasMessage) {
$button->setAttribute('data-state', 'open');
}
return array(
$this->targetFragment => '<p class="grid-csv-button">'. $button->Field() . '</p>'
@ -65,4 +104,58 @@ class GridFieldImportButton implements GridField_HTMLProvider
{
return [];
}
/**
* @return string
*/
public function getModalTitle()
{
return $this->modalTitle;
}
/**
* @param string $modalTitle
* @return $this
*/
public function setModalTitle($modalTitle)
{
$this->modalTitle = $modalTitle;
return $this;
}
/**
* @return Form
*/
public function getImportForm()
{
return $this->importForm;
}
/**
* @param Form $importForm
* @return $this
*/
public function setImportForm($importForm)
{
$this->importForm = $importForm;
return $this;
}
/**
* @return string
*/
public function getImportIframe()
{
return $this->importIframe;
}
/**
* @param string $importIframe
* @return $this
*/
public function setImportIframe($importIframe)
{
$this->importIframe = $importIframe;
return $this;
}
}

View File

@ -1,15 +1,21 @@
<div class="modal fade grid-field-import" tabindex="-1" role="dialog">
<div id="$ImportModalID.ATT" class="modal fade grid-field-import" tabindex="-1" role="dialog">
<div class="modal-dialog" role="document">
<div class="modal-content"><div class="modal-header">
<button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>
</div>
<div class="modal-body">
<% if $URL %>
<iframe src="$URL" id="MemberImportFormIframe" width="100%%" height="400px" frameBorder="0"></iframe>
<% else %>
$ImportForm
<% end_if %>
<div class="modal-content">
<div class="modal-header">
<% if $ImportModalTitle %>
<h2 class="modal-title">$ImportModalTitle</h2>
<% end_if %>
<button type="button" class="close" data-dismiss="modal" aria-label="Close">
<span aria-hidden="true">×</span>
</button>
</div>
<div class="modal-body">
<% if $ImportIframe %>
<iframe src="$ImportIframe.ATT" width="100%%" height="400px" frameBorder="0"></iframe>
<% else_if $ImportForm %>
$ImportForm
<% end_if %>
</div>
</div>
</div>
</div>