mirror of
https://github.com/silverstripe/silverstripe-reports
synced 2024-10-22 09:05:53 +00:00
Updated ModelAdmin.js to use $ instead of jQuery for executing jQuery items.
Added support for specifying a SearchClassSelector() method on ModelAdmin, which returns 'tabs' or 'dropdown', to enable a better UI for ModelAdmins that manage a large number of different classes. Updated ModelAdmin's javascript to provide more status indicator (loading icons, success/failure messages), using HTTP status codes and custom status text. git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@62334 467b73ca-7a2a-4603-9d3b-597d59a354a9
This commit is contained in:
parent
ad6ed7f1a7
commit
dcb9e53ed0
@ -159,6 +159,18 @@ abstract class ModelAdmin extends LeftAndMain {
|
|||||||
return new $class($this, $model);
|
return new $class($this, $model);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method can be overloaded to specify the UI by which the search class is chosen.
|
||||||
|
*
|
||||||
|
* It can create a tab strip or a dropdown. The dropdown is useful when there are a large number of classes.
|
||||||
|
* By default, it will show a tabs for 1-3 classes, and a dropdown for 4 or more classes.
|
||||||
|
*
|
||||||
|
* @return String: 'tabs' or 'dropdown'
|
||||||
|
*/
|
||||||
|
public function SearchClassSelector() {
|
||||||
|
return sizeof($this->getManagedModels()) > 3 ? 'dropdown' : 'tabs';
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Allows to choose which record needs to be created.
|
* Allows to choose which record needs to be created.
|
||||||
*
|
*
|
||||||
@ -427,6 +439,7 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
$model = singleton($this->modelClass);
|
$model = singleton($this->modelClass);
|
||||||
|
|
||||||
$source = $model->summaryFields();
|
$source = $model->summaryFields();
|
||||||
|
$value = array();
|
||||||
if($source) foreach ($source as $fieldName => $label){
|
if($source) foreach ($source as $fieldName => $label){
|
||||||
$value[] = $fieldName;
|
$value[] = $fieldName;
|
||||||
}
|
}
|
||||||
@ -474,8 +487,17 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
* @return string
|
* @return string
|
||||||
*/
|
*/
|
||||||
function search($request, $form) {
|
function search($request, $form) {
|
||||||
|
// Get the results form to be rendered
|
||||||
$resultsForm = $this->ResultsForm($form->getData());
|
$resultsForm = $this->ResultsForm($form->getData());
|
||||||
return $resultsForm->forTemplate();
|
// Before rendering, let's get the total number of results returned
|
||||||
|
$tableField = $resultsForm->Fields()->fieldByName($this->modelClass);
|
||||||
|
$numResults = $tableField->TotalCount();
|
||||||
|
|
||||||
|
if($numResults) {
|
||||||
|
return new HTTPResponse($resultsForm->forTemplate(), 200, "Your search found $numResults matching items");
|
||||||
|
} else {
|
||||||
|
return new HTTPResponse($resultsForm->forTemplate(), 404, "Your search didn't return any matching items");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -557,7 +579,7 @@ class ModelAdmin_CollectionController extends Controller {
|
|||||||
* @return unknown
|
* @return unknown
|
||||||
*/
|
*/
|
||||||
function add($request) {
|
function add($request) {
|
||||||
return $this->AddForm()->forAjaxTemplate();
|
return new HTTPResponse($this->AddForm()->forAjaxTemplate(), 200, "Fill out this form to add a $this->modelClass to the database.");
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -626,7 +648,7 @@ class ModelAdmin_RecordController extends Controller {
|
|||||||
function edit($request) {
|
function edit($request) {
|
||||||
if ($this->currentRecord) {
|
if ($this->currentRecord) {
|
||||||
if(Director::is_ajax()) {
|
if(Director::is_ajax()) {
|
||||||
return new HTTPResponse($this->EditForm()->forAjaxTemplate(), 200, "Page loaded");
|
return new HTTPResponse($this->EditForm()->forAjaxTemplate(), 200, "Loaded '" . $this->currentRecord->Title . "' for editing.");
|
||||||
} else {
|
} else {
|
||||||
// This is really quite ugly; to fix will require a change in the way that customise() works. :-(
|
// This is really quite ugly; to fix will require a change in the way that customise() works. :-(
|
||||||
return $this->parentController->parentController->customise(array(
|
return $this->parentController->parentController->customise(array(
|
||||||
|
@ -31,6 +31,12 @@ body.ModelAdmin #SearchForm_holder {
|
|||||||
margin-bottom:1em;
|
margin-bottom:1em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body.ModelAdmin #ModelClassSelector {
|
||||||
|
border-bottom: 1px #AAA solid;
|
||||||
|
padding-bottom: 0.5em;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
body.ModelAdmin #SearchForm_holder div.ResultAssemblyBlock{
|
body.ModelAdmin #SearchForm_holder div.ResultAssemblyBlock{
|
||||||
margin-bottom: 1em;
|
margin-bottom: 1em;
|
||||||
}
|
}
|
||||||
|
@ -76,6 +76,11 @@ li.action button {
|
|||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
input.action.loading, input.action.loading:hover {
|
||||||
|
padding-left: 22px;
|
||||||
|
background: url(../images/network-save.gif) 3px 2px no-repeat;
|
||||||
|
}
|
||||||
|
|
||||||
input.delete:hover,
|
input.delete:hover,
|
||||||
button.delete:hover,
|
button.delete:hover,
|
||||||
.actionparams input.delete:hover {
|
.actionparams input.delete:hover {
|
||||||
|
@ -8,16 +8,40 @@
|
|||||||
* @todo add live query to manage application of events to DOM refreshes
|
* @todo add live query to manage application of events to DOM refreshes
|
||||||
* @todo alias the $ function instead of literal jQuery
|
* @todo alias the $ function instead of literal jQuery
|
||||||
*/
|
*/
|
||||||
jQuery(document).ready(function() {
|
(function($) {
|
||||||
|
$(document).ready(function() {
|
||||||
/**
|
/**
|
||||||
* Attach tabs plugin to the set of search filter and edit forms
|
* Attach tabs plugin to the set of search filter and edit forms
|
||||||
*/
|
*/
|
||||||
jQuery('ul.tabstrip').tabs();
|
$('ul.tabstrip').tabs();
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Highlight buttons on click
|
||||||
|
*/
|
||||||
|
$('input[type=submit]').click(function() {
|
||||||
|
$(this).addClass('loading');
|
||||||
|
});
|
||||||
|
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
// Search form
|
// Search form
|
||||||
//////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If a dropdown is used to choose between the classes, it is handled by this code
|
||||||
|
*/
|
||||||
|
$('#ModelClassSelector select')
|
||||||
|
// Set up an onchange function to show the applicable form and hide all others
|
||||||
|
.change(function() {
|
||||||
|
var $selector = $(this);
|
||||||
|
$('option', this).each(function() {
|
||||||
|
var $form = $('#'+$(this).val());
|
||||||
|
if($selector.val() == $(this).val()) $form.show();
|
||||||
|
else $form.hide();
|
||||||
|
});
|
||||||
|
})
|
||||||
|
// Initialise the form by calling this onchange event straight away
|
||||||
|
.change();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Stores a jQuery reference to the last submitted search form.
|
* Stores a jQuery reference to the last submitted search form.
|
||||||
*/
|
*/
|
||||||
@ -29,45 +53,47 @@ jQuery(document).ready(function() {
|
|||||||
*
|
*
|
||||||
* @todo use livequery to manage ResultTable click handlers
|
* @todo use livequery to manage ResultTable click handlers
|
||||||
*/
|
*/
|
||||||
jQuery('#SearchForm_holder .tab form').submit(function(){
|
$('#SearchForm_holder .tab form').submit(function () {
|
||||||
__lastSearch = jQuery(this);
|
var $form = $(this);
|
||||||
|
$('#ModelAdminPanel').load($(this).attr('action'), $(this).formToArray(), standardStatusHandler(function(result) {
|
||||||
form = jQuery(this);
|
__lastSearch = $form;
|
||||||
data = formData(form);
|
$('#form_actions_right').remove();
|
||||||
jQuery.get(form.attr('action'), data, function(result){
|
|
||||||
jQuery('#right #ModelAdminPanel').html(result);
|
|
||||||
jQuery('#form_actions_right').remove();
|
|
||||||
Behaviour.apply();
|
Behaviour.apply();
|
||||||
});
|
// Remove the loading indicators from the buttons
|
||||||
|
$('input[type=submit]', $form).removeClass('loading');
|
||||||
|
},
|
||||||
|
// Failure handler - we should still remove loading indicator
|
||||||
|
function () {
|
||||||
|
$('input[type=submit]', $form).removeClass('loading');
|
||||||
|
}));
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Clear search button
|
* Clear search button
|
||||||
*/
|
*/
|
||||||
jQuery('#SearchForm_holder button[name=action_clearsearch]').click(function(e) {
|
$('#SearchForm_holder button[name=action_clearsearch]').click(function(e) {
|
||||||
jQuery(this.form).clearForm();
|
$(this.form).clearForm();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Column selection in search form
|
* Column selection in search form
|
||||||
*/
|
*/
|
||||||
jQuery('a.form_frontend_function.toggle_result_assembly').click(function(){
|
$('a.form_frontend_function.toggle_result_assembly').click(function(){
|
||||||
var toggleElement = jQuery(this).next();
|
var toggleElement = $(this).next();
|
||||||
toggleElement.toggle();
|
toggleElement.toggle();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
jQuery('a.form_frontend_function.tick_all_result_assembly').click(function(){
|
$('a.form_frontend_function.tick_all_result_assembly').click(function(){
|
||||||
var resultAssembly = jQuery('div#ResultAssembly ul li input');
|
var resultAssembly = $('div#ResultAssembly ul li input');
|
||||||
resultAssembly.attr('checked', 'checked');
|
resultAssembly.attr('checked', 'checked');
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
jQuery('a.form_frontend_function.untick_all_result_assembly').click(function(){
|
$('a.form_frontend_function.untick_all_result_assembly').click(function(){
|
||||||
var resultAssembly = jQuery('div#ResultAssembly ul li input');
|
var resultAssembly = $('div#ResultAssembly ul li input');
|
||||||
resultAssembly.removeAttr('checked');
|
resultAssembly.removeAttr('checked');
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -79,18 +105,19 @@ jQuery(document).ready(function() {
|
|||||||
/**
|
/**
|
||||||
* Table record handler for search result record
|
* Table record handler for search result record
|
||||||
*/
|
*/
|
||||||
jQuery('#right #Form_ResultsForm tbody td a')
|
$('#right #Form_ResultsForm tbody td a')
|
||||||
.livequery('click', function(){
|
.livequery('click', function(){
|
||||||
var el = jQuery(this);
|
$(this).parent().parent().addClass('loading');
|
||||||
|
var el = $(this);
|
||||||
showRecord(el.attr('href'));
|
showRecord(el.attr('href'));
|
||||||
return false;
|
return false;
|
||||||
})
|
})
|
||||||
.hover(
|
.hover(
|
||||||
function(){
|
function(){
|
||||||
jQuery(this).addClass('over').siblings().addClass('over')
|
$(this).addClass('over').siblings().addClass('over')
|
||||||
},
|
},
|
||||||
function(){
|
function(){
|
||||||
jQuery(this).removeClass('over').siblings().removeClass('over')
|
$(this).removeClass('over').siblings().removeClass('over')
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -101,7 +128,9 @@ jQuery(document).ready(function() {
|
|||||||
/**
|
/**
|
||||||
* RHS panel Back button
|
* RHS panel Back button
|
||||||
*/
|
*/
|
||||||
jQuery('#Form_EditForm_action_goBack').livequery('click', function() {
|
$('#Form_EditForm_action_goBack').livequery('click', function() {
|
||||||
|
$(this).addClass('loading');
|
||||||
|
|
||||||
if(__lastSearch) __lastSearch.trigger('submit');
|
if(__lastSearch) __lastSearch.trigger('submit');
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -109,20 +138,22 @@ jQuery(document).ready(function() {
|
|||||||
/**
|
/**
|
||||||
* RHS panel Save button
|
* RHS panel Save button
|
||||||
*/
|
*/
|
||||||
jQuery('#right #form_actions_right input[name=action_doSave]').livequery('click', function(){
|
$('#right #form_actions_right input[name=action_doSave]').livequery('click', function(){
|
||||||
var form = jQuery('#right form');
|
$(this).addClass('loading');
|
||||||
var formAction = form.attr('action') + '?' + jQuery(this).fieldSerialize();
|
|
||||||
|
var form = $('#right form');
|
||||||
|
var formAction = form.attr('action') + '?' + $(this).fieldSerialize();
|
||||||
|
|
||||||
// Post the data to save
|
// Post the data to save
|
||||||
jQuery.post(formAction, formData(form), function(result){
|
$.post(formAction, formData(form), function(result){
|
||||||
jQuery('#right #ModelAdminPanel').html(result);
|
$('#right #ModelAdminPanel').html(result);
|
||||||
|
|
||||||
statusMessage("Saved");
|
statusMessage("Saved");
|
||||||
|
|
||||||
// TODO/SAM: It seems a bit of a hack to have to list all the little updaters here.
|
// TODO/SAM: It seems a bit of a hack to have to list all the little updaters here.
|
||||||
// Is livequery a solution?
|
// Is livequery a solution?
|
||||||
Behaviour.apply(); // refreshes ComplexTableField
|
Behaviour.apply(); // refreshes ComplexTableField
|
||||||
jQuery('#right ul.tabstrip').tabs();
|
$('#right ul.tabstrip').tabs();
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -131,24 +162,26 @@ jQuery(document).ready(function() {
|
|||||||
/**
|
/**
|
||||||
* RHS panel Delete button
|
* RHS panel Delete button
|
||||||
*/
|
*/
|
||||||
jQuery('#right #form_actions_right input[name=action_doDelete]').livequery('click', function(){
|
$('#right #form_actions_right input[name=action_doDelete]').livequery('click', function(){
|
||||||
var confirmed = confirm("Do you really want to delete?");
|
var confirmed = confirm("Do you really want to delete?");
|
||||||
if(!confirmed) return false;
|
if(!confirmed) return false;
|
||||||
|
|
||||||
var form = jQuery('#right form');
|
$(this).addClass('loading');
|
||||||
var formAction = form.attr('action') + '?' + jQuery(this).fieldSerialize();
|
var form = $('#right form');
|
||||||
|
var formAction = form.attr('action') + '?' + $(this).fieldSerialize();
|
||||||
|
|
||||||
// The POST actually handles the delete
|
// The POST actually handles the delete
|
||||||
jQuery.post(formAction, formData(form), function(result){
|
$.post(formAction, formData(form), function(result){
|
||||||
// On success, the panel is refreshed and a status message shown.
|
// On success, the panel is refreshed and a status message shown.
|
||||||
jQuery('#right #ModelAdminPanel').html(result);
|
$('#right #ModelAdminPanel').html(result);
|
||||||
|
|
||||||
statusMessage("Deleted");
|
statusMessage("Deleted");
|
||||||
|
$('#form_actions_right').remove();
|
||||||
|
|
||||||
// TODO/SAM: It seems a bit of a hack to have to list all the little updaters here.
|
// TODO/SAM: It seems a bit of a hack to have to list all the little updaters here.
|
||||||
// Is livequery a solution?
|
// Is livequery a solution?
|
||||||
Behaviour.apply(); // refreshes ComplexTableField
|
Behaviour.apply(); // refreshes ComplexTableField
|
||||||
jQuery('#right ul.tabstrip').tabs();
|
$('#right ul.tabstrip').tabs();
|
||||||
});
|
});
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -162,9 +195,9 @@ jQuery(document).ready(function() {
|
|||||||
/**
|
/**
|
||||||
* Add object button
|
* Add object button
|
||||||
*/
|
*/
|
||||||
jQuery('#Form_ManagedModelsSelect').submit(function(){
|
$('#Form_ManagedModelsSelect').submit(function(){
|
||||||
className = jQuery('select option:selected', this).val();
|
className = $('select option:selected', this).val();
|
||||||
requestPath = jQuery(this).attr('action').replace('ManagedModelsSelect', className + '/add');
|
requestPath = $(this).attr('action').replace('ManagedModelsSelect', className + '/add');
|
||||||
showRecord(requestPath);
|
showRecord(requestPath);
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
@ -172,9 +205,9 @@ jQuery(document).ready(function() {
|
|||||||
/**
|
/**
|
||||||
* Toggle import specifications
|
* Toggle import specifications
|
||||||
*/
|
*/
|
||||||
jQuery('#Form_ImportForm_holder .spec .details').hide();
|
$('#Form_ImportForm_holder .spec .details').hide();
|
||||||
jQuery('#Form_ImportForm_holder .spec a.detailsLink').click(function() {
|
$('#Form_ImportForm_holder .spec a.detailsLink').click(function() {
|
||||||
jQuery('#' + jQuery(this).attr('href').replace(/.*#/,'')).toggle();
|
$('#' + $(this).attr('href').replace(/.*#/,'')).toggle();
|
||||||
return false;
|
return false;
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -187,10 +220,10 @@ jQuery(document).ready(function() {
|
|||||||
* @todo Should this be turned into a method on the #Form_EditForm using effen or something?
|
* @todo Should this be turned into a method on the #Form_EditForm using effen or something?
|
||||||
*/
|
*/
|
||||||
function showRecord(uri) {
|
function showRecord(uri) {
|
||||||
jQuery('#right #ModelAdminPanel').load(uri, standardStatusHandler(function(result) {
|
$('#right #ModelAdminPanel').load(uri, standardStatusHandler(function(result) {
|
||||||
jQuery('#SearchForm_holder').tabs();
|
$('#SearchForm_holder').tabs();
|
||||||
Behaviour.apply(); // refreshes ComplexTableField
|
Behaviour.apply(); // refreshes ComplexTableField
|
||||||
jQuery('#right ul.tabstrip').tabs();
|
$('#right ul.tabstrip').tabs();
|
||||||
}));
|
}));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -200,8 +233,8 @@ jQuery(document).ready(function() {
|
|||||||
*/
|
*/
|
||||||
function formData(scope) {
|
function formData(scope) {
|
||||||
var data = {};
|
var data = {};
|
||||||
jQuery('*[name]', scope).each(function(){
|
$('*[name]', scope).each(function(){
|
||||||
var t = jQuery(this);
|
var t = $(this);
|
||||||
if(t.attr('type') != 'checkbox' || t.attr('checked') == true) {
|
if(t.attr('type') != 'checkbox' || t.attr('checked') == true) {
|
||||||
data[t.attr('name')] = t.val();
|
data[t.attr('name')] = t.val();
|
||||||
}
|
}
|
||||||
@ -220,18 +253,26 @@ jQuery(document).ready(function() {
|
|||||||
* Pass it as this:
|
* Pass it as this:
|
||||||
* standardStatusHandler(function(response) { ... })
|
* standardStatusHandler(function(response) { ... })
|
||||||
*/
|
*/
|
||||||
function standardStatusHandler(callback) {
|
function standardStatusHandler(callback, failureCallback) {
|
||||||
return function(response, status, xhr) {
|
return function(response, status, xhr) {
|
||||||
|
// If the response is takne from $.ajax's complete handler, then swap the variables around
|
||||||
|
if(response.status) {
|
||||||
|
xhr = response;
|
||||||
|
response = xhr.responseText;
|
||||||
|
}
|
||||||
|
|
||||||
if(status == 'success') {
|
if(status == 'success') {
|
||||||
statusMessage(xhr.statusText, "good");
|
statusMessage(xhr.statusText, "good");
|
||||||
callback(response, status, xhr);
|
$(this).each(callback, [response, status, xhr]);
|
||||||
} else {
|
} else {
|
||||||
statusMessage(xhr.statusText, "bad");
|
statusMessage(xhr.statusText, "bad");
|
||||||
|
if(failureCallback) $(this).each(failureCallback, [response, status, xhr]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
})
|
||||||
|
})(jQuery);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @todo Terrible HACK, but thats the cms UI...
|
* @todo Terrible HACK, but thats the cms UI...
|
||||||
|
@ -1,11 +1,25 @@
|
|||||||
<div id="LeftPane">
|
<div id="LeftPane">
|
||||||
<h2><% _t('SEARCHLISTINGS','Search Listings') %></h2>
|
<h2><% _t('SEARCHLISTINGS','Search Listings') %></h2>
|
||||||
<div id="SearchForm_holder" class="leftbottom">
|
<div id="SearchForm_holder" class="leftbottom">
|
||||||
|
<% if SearchClassSelector = tabs %>
|
||||||
<ul class="tabstrip">
|
<ul class="tabstrip">
|
||||||
<% control SearchForms %>
|
<% control SearchForms %>
|
||||||
<li class="first"><a href="#{$Form.Name}_$ClassName">$Title</a></li>
|
<li class="$FirstLast"><a href="#{$Form.Name}_$ClassName">$Title</a></li>
|
||||||
<% end_control %>
|
<% end_control %>
|
||||||
</ul>
|
</ul>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
|
<% if SearchClassSelector = dropdown %>
|
||||||
|
<p id="ModelClassSelector">
|
||||||
|
Search for:
|
||||||
|
<select>
|
||||||
|
<% control SearchForms %>
|
||||||
|
<option value="{$Form.Name}_$ClassName">$Title</option>
|
||||||
|
<% end_control %>
|
||||||
|
</select>
|
||||||
|
</p>
|
||||||
|
<% end_if %>
|
||||||
|
|
||||||
<% control SearchForms %>
|
<% control SearchForms %>
|
||||||
<div class="tab" id="{$Form.Name}_$ClassName">
|
<div class="tab" id="{$Form.Name}_$ClassName">
|
||||||
$Form
|
$Form
|
||||||
|
Loading…
x
Reference in New Issue
Block a user