Skip to content

Commit df7713f

Browse files
committed
ENHANCEMENT Initial 'concrete' implementation of CMS forms incl. ajax saving, validation placeholders and session-pinging
MINOR Removed 'innerLayout' from CMSMain.js javascript git-svn-id: svn://svn.silverstripe.com/silverstripe/open/modules/cms/trunk@92599 467b73ca-7a2a-4603-9d3b-597d59a354a9
1 parent 1b312ae commit df7713f

File tree

2 files changed

+284
-62
lines changed

2 files changed

+284
-62
lines changed

javascript/CMSMain.js

Lines changed: 61 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,68 @@
11
var outerLayout;
2-
var innerLayout;
32

4-
jQuery(document).ready(function () {
5-
6-
// layout containing the tree, CMS menu, the main form etc.
7-
outerLayout = jQuery('body').layout({
8-
defaults: {
9-
// TODO Reactivate once we have localized values
10-
togglerTip_open: '',
11-
togglerTip_closed: '',
12-
resizerTip: '',
13-
sliderTip: ''
14-
},
15-
// contains CMSMenu
16-
north: {
17-
slidable: false,
18-
resizable: false,
19-
size: 35,
20-
togglerLength_open: 0
21-
},
22-
// "Page view", "profile" and "logout" links
23-
south: {
24-
slidable: false,
25-
resizable: false,
26-
size: 20,
27-
togglerLength_open: 0
28-
},
29-
// "Insert link" etc.
30-
east: {
31-
initClosed: true,
32-
fxName: "none"
33-
},
34-
// Tree, page version history
35-
west: {
36-
size: 250,
37-
onresize: function () { jQuery("#treepanes").accordion("resize"); },
38-
onopen: function () { jQuery("#treepanes").accordion("resize"); },
39-
fxName: "none"
3+
(function($) {
4+
$('body.CMSMain').concrete({ss:{cmsMain:{
5+
mainLayout: null,
6+
7+
onmatch: function() {
8+
this.mainLayout = this.ss().cmsMain()._setupLayout();
409
},
41-
// Page forms
42-
center: {
43-
onresize: "innerLayout.resizeAll"
10+
11+
/**
12+
* Initialize jQuery layout manager with the following panes:
13+
* - east: Tree, Page Version History, Site Reports
14+
* - center: Form
15+
* - west: "Insert Image", "Insert Link", "Insert Flash" panes
16+
* - north: CMS area menu bar
17+
* - south: "Page view", "profile" and "logout" links
18+
*/
19+
_setupLayout: function() {
20+
// layout containing the tree, CMS menu, the main form etc.
21+
var layout = $('body').layout({
22+
defaults: {
23+
// TODO Reactivate once we have localized values
24+
togglerTip_open: '',
25+
togglerTip_closed: '',
26+
resizerTip: '',
27+
sliderTip: ''
28+
},
29+
north: {
30+
slidable: false,
31+
resizable: false,
32+
size: 35,
33+
togglerLength_open: 0
34+
},
35+
south: {
36+
slidable: false,
37+
resizable: false,
38+
size: 20,
39+
togglerLength_open: 0
40+
},
41+
east: {
42+
initClosed: true,
43+
fxName: "none"
44+
},
45+
west: {
46+
size: 250,
47+
onresize: function () { $("#treepanes").accordion("resize"); },
48+
onopen: function () { $("#treepanes").accordion("resize"); },
49+
fxName: "none"
50+
},
51+
center: {}
52+
});
53+
54+
// Adjust tree accordion etc. in left panel to work correctly
55+
// with jQuery.layout (see http://layout.jquery-dev.net/tips.html#Widget_Accordion)
56+
this.find("#treepanes").accordion({
57+
fillSpace: true,
58+
animated: false
59+
});
60+
61+
return layout;
4462
}
45-
});
63+
}}});
4664

47-
// Layout for the form and its buttons
48-
innerLayout = jQuery('#right').layout({
49-
center: {},
50-
south: {
51-
slidable: false,
52-
resizable: false,
53-
size: 30,
54-
togglerLength_open: 0
55-
}
65+
$('#Form_EditForm').concrete({
5666

5767
})
58-
59-
// Adjust tree accordion etc. in left panel to work correctly
60-
// with jQuery.layout (see http://layout.jquery-dev.net/tips.html#Widget_Accordion)
61-
jQuery("#treepanes").accordion({
62-
fillSpace: true,
63-
animated: false
64-
});
65-
66-
});
68+
})(jQuery);

javascript/LeftAndMain.js

Lines changed: 223 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,225 @@
1+
(function($) {
2+
3+
/**
4+
* Main LeftAndMain interface with some control
5+
* panel and an edit form.
6+
*
7+
* Events:
8+
* - beforeSave
9+
* - afterSave
10+
* - beforeValidate
11+
* - afterValidate
12+
*/
13+
$('.LeftAndMain').concrete({ss:{leftAndMain:{
14+
onmatch: function() {
15+
this.ss().leftAndMain()._setupPinging();
16+
this.ss().leftAndMain()._setupButtons();
17+
},
18+
19+
_setupPinging: function() {
20+
var pingIntervalSeconds = 5*60;
21+
22+
// setup pinging for login expiry
23+
setInterval(function() {
24+
$.get("Security/ping");
25+
}, pingIntervalSeconds * 1000);
26+
},
27+
28+
/**
29+
* Make all buttons "hoverable" with jQuery theming.
30+
*/
31+
_setupButtons: function() {
32+
// Initialize buttons
33+
this.find(':submit, button').livequery(function() {
34+
jQuery(this).addClass(
35+
'ui-state-default ' +
36+
'ui-corner-all'
37+
)
38+
.hover(
39+
function() {
40+
$(this).addClass('ui-state-hover');
41+
},
42+
function() {
43+
$(this).removeClass('ui-state-hover');
44+
}
45+
)
46+
.focus(function() {
47+
$(this).addClass('ui-state-focus');
48+
})
49+
.blur(function() {
50+
$(this).removeClass('ui-state-focus');
51+
});
52+
});
53+
}
54+
}}});
55+
56+
/**
57+
* Base edit form, provides ajaxified saving
58+
* and reloading itself through the ajax return values.
59+
* Takes care of resizing tabsets within the layout container.
60+
*/
61+
$('#Form_EditForm').concrete({ss:{
62+
onmatch: function() {
63+
var $this = this;
64+
// artificially delay the resize event 200ms
65+
// to avoid overlapping height changes in different onresize() methods
66+
$(window).resize(function () {
67+
var timerID = "timerLayout_"+this.id;
68+
if (window[timerID]) clearTimeout(window[timerID]);
69+
window[timerID] = setTimeout(function() {$this.ss()._resizeChildren();}, 200);
70+
}).trigger('resize');
71+
72+
// trigger resize whenever new tabs are shown
73+
// @todo This is called multiple times when tabs are loaded
74+
this.find('.ss-tabset').bind('tabsshow', function() {$this.ss()._resizeChildren();});
75+
},
76+
77+
/**
78+
* Suppress submission unless it is handled through save()
79+
*/
80+
onsubmit: function(e) {
81+
return false;
82+
},
83+
84+
/**
85+
* @param DOMElement button The pressed button (optiona)
86+
*/
87+
ajaxSubmit: function(button) {
88+
// default to first button if none given - simulates browser behaviour
89+
if(!button) button = this.find(':submit:first');
90+
91+
var $form = this;
92+
93+
this.trigger('beforeSubmit', [button]);
94+
95+
// set button to "submitting" state
96+
$(button).addClass('loading');
97+
98+
// @todo TinyMCE coupling
99+
if(typeof tinyMCE != 'undefined') tinyMCE.triggerSave();
100+
101+
// validate if required
102+
if(!this.ss().validate()) {
103+
this.trigger('validationError', [button]);
104+
105+
// TODO Automatically switch to the tab/position of the first error
106+
statusMessage("Validation failed.", "bad");
107+
108+
if($('Form_EditForm_action_save') && $('Form_EditForm_action_save').stopLoading) $('Form_EditForm_action_save').stopLoading();
109+
110+
return false;
111+
}
112+
113+
// get all data from the form
114+
var data = this.serializeArray();
115+
// add button action
116+
data.push({name: $(button).attr('name'), value:'1'});
117+
$.post(
118+
this.attr('action'),
119+
data,
120+
function(result) {
121+
$(button).removeClass('loading');
122+
123+
$form.trigger('afterSubmit', [result]);
124+
125+
$form.ss().loadNewPage();
126+
},
127+
// @todo Currently all responses are assumed to be evaluated
128+
'script'
129+
);
130+
131+
return false;
132+
},
133+
134+
/**
135+
* Hook in (optional) validation routines.
136+
* Currently clientside validation is not supported out of the box in the CMS.
137+
*
138+
* @return boolean
139+
*/
140+
validate: function() {
141+
this.trigger('beforeValidate');
142+
var isValid = true;
143+
this.trigger('afterValidate', [isValid]);
144+
145+
return isValid;
146+
},
147+
148+
loadNewPage: function(result) {
149+
// TinyMCE coupling
150+
if(typeof tinymce_removeAll != 'undefined') tinymce_removeAll();
151+
152+
// Rewrite # links
153+
result = result.replace(/(<a[^>]+href *= *")#/g, '$1' + window.location.href.replace(/#.*$/,'') + '#');
154+
155+
// Rewrite iframe links (for IE)
156+
result = result.replace(/(<iframe[^>]*src=")([^"]+)("[^>]*>)/g, '$1' + $('base').attr('href') + '$2$3');
157+
158+
// Prepare iframes for removal, otherwise we get loading bugs
159+
this.find('iframe').each(function() {
160+
this.contentWindow.location.href = 'about:blank';
161+
this.remove();
162+
})
163+
164+
this.html(result);
165+
166+
if(this.hasClass('validationerror')) {
167+
statusMessage(ss.i18n._t('ModelAdmin.VALIDATIONERROR', 'Validation Error'), 'bad');
168+
} else {
169+
statusMessage(ss.i18n._t('ModelAdmin.SAVED', 'Saved'), 'good');
170+
}
171+
172+
Behaviour.apply(); // refreshes ComplexTableField
173+
174+
// If there's a title field and it's got a "new XX" value, focus/select that first
175+
// This is really a little too CMS-specific (as opposed to LeftAndMain), but the cleanup can happen after jQuery refactoring
176+
if($('input#Form_EditForm_Title') && $('input#Form_EditForm_Title').value.match(/^new/i)) {
177+
$('input#Form_EditForm_Title').select();
178+
}
179+
},
180+
181+
/**
182+
* Resize elements in center panel
183+
* to fit the boundary box provided by the layout manager
184+
*/
185+
_resizeChildren: function() {
186+
this.fitHeightToParent();
187+
$('fieldset', this).fitHeightToParent();
188+
// Order of resizing is important: Outer to inner
189+
// TODO Only supports two levels of tabs at the moment
190+
$('fieldset > .ss-tabset', this).fitHeightToParent();
191+
$('fieldset > .ss-tabset > .tab', this).fitHeightToParent();
192+
$('fieldset > .ss-tabset > .tab > .ss-tabset', this).fitHeightToParent();
193+
$('fieldset > .ss-tabset > .tab > .ss-tabset > .tab', this).fitHeightToParent();
194+
}
195+
}});
196+
197+
$('#Form_EditForm .Actions :submit').concrete({ss:{
198+
onclick: function(e) {
199+
$(this[0].form).ss().ajaxSubmit(this);
200+
return false;
201+
}
202+
}});
203+
})(jQuery);
204+
205+
jQuery(document).ready(function() {
206+
// @todo remove
207+
jQuery.concrete.triggerMatching();
208+
});
209+
210+
211+
212+
213+
214+
215+
216+
217+
218+
219+
220+
221+
222+
1223
var _AJAX_LOADING = false;
2224

3225
Behaviour.register({
@@ -442,9 +664,7 @@ function hideIndicator(id) {
442664
Effect.Fade(id, {duration: 0.3});
443665
}
444666

445-
setInterval(function() {
446-
new Ajax.Request("Security/ping");
447-
}, 180*1000);
667+
448668

449669
/**
450670
* Find and enable TinyMCE on all htmleditor fields

0 commit comments

Comments
 (0)