##// END OF EJS Templates
Send the CSRF token with Ajax requests (#7843)....
Jean-Philippe Lang -
r5014:36dbb3906b32
parent child
Show More
@@ -1,81 +1,82
1 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
2 2 <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
3 3 <head>
4 4 <meta http-equiv="content-type" content="text/html; charset=utf-8" />
5 5 <title><%=h html_title %></title>
6 6 <meta name="description" content="<%= Redmine::Info.app_name %>" />
7 7 <meta name="keywords" content="issue,bug,tracker" />
8 <%= csrf_meta_tag %>
8 9 <%= favicon %>
9 10 <%= stylesheet_link_tag 'application', :media => 'all' %>
10 11 <%= stylesheet_link_tag 'rtl', :media => 'all' if l(:direction) == 'rtl' %>
11 12 <%= javascript_heads %>
12 13 <%= heads_for_theme %>
13 14 <%= heads_for_wiki_formatter %>
14 15 <!--[if IE 6]>
15 16 <style type="text/css">
16 17 * html body{ width: expression( document.documentElement.clientWidth < 900 ? '900px' : '100%' ); }
17 18 body {behavior: url(<%= stylesheet_path "csshover.htc" %>);}
18 19 </style>
19 20 <![endif]-->
20 21 <%= call_hook :view_layouts_base_html_head %>
21 22 <!-- page specific tags -->
22 23 <%= yield :header_tags -%>
23 24 </head>
24 25 <body class="<%=h body_css_classes %>">
25 26 <div id="wrapper">
26 27 <div id="wrapper2">
27 28 <div id="top-menu">
28 29 <div id="account">
29 30 <%= render_menu :account_menu -%>
30 31 </div>
31 32 <%= content_tag('div', "#{l(:label_logged_as)} #{link_to_user(User.current, :format => :username)}", :id => 'loggedas') if User.current.logged? %>
32 33 <%= render_menu :top_menu if User.current.logged? || !Setting.login_required? -%>
33 34 </div>
34 35
35 36 <div id="header">
36 37 <% if User.current.logged? || !Setting.login_required? %>
37 38 <div id="quick-search">
38 39 <% form_tag({:controller => 'search', :action => 'index', :id => @project}, :method => :get ) do %>
39 40 <%= hidden_field_tag(controller.default_search_scope, 1, :id => nil) if controller.default_search_scope %>
40 41 <%= link_to l(:label_search), {:controller => 'search', :action => 'index', :id => @project}, :accesskey => accesskey(:search) %>:
41 42 <%= text_field_tag 'q', @question, :size => 20, :class => 'small', :accesskey => accesskey(:quick_search) %>
42 43 <% end %>
43 44 <%= render_project_jump_box %>
44 45 </div>
45 46 <% end %>
46 47
47 48 <h1><%= page_header_title %></h1>
48 49
49 50 <% if display_main_menu?(@project) %>
50 51 <div id="main-menu">
51 52 <%= render_main_menu(@project) %>
52 53 </div>
53 54 <% end %>
54 55 </div>
55 56
56 57 <%= tag('div', {:id => 'main', :class => (has_content?(:sidebar) ? '' : 'nosidebar')}, true) %>
57 58 <div id="sidebar">
58 59 <%= yield :sidebar %>
59 60 <%= call_hook :view_layouts_base_sidebar %>
60 61 </div>
61 62
62 63 <div id="content">
63 64 <%= render_flash_messages %>
64 65 <%= yield %>
65 66 <%= call_hook :view_layouts_base_content %>
66 67 <div style="clear:both;"></div>
67 68 </div>
68 69 </div>
69 70
70 71 <div id="ajax-indicator" style="display:none;"><span><%= l(:label_loading) %></span></div>
71 72
72 73 <div id="footer">
73 74 <div class="bgl"><div class="bgr">
74 75 Powered by <%= link_to Redmine::Info.app_name, Redmine::Info.url %> &copy; 2006-2011 Jean-Philippe Lang
75 76 </div></div>
76 77 </div>
77 78 </div>
78 79 </div>
79 80 <%= call_hook :view_layouts_base_body_bottom %>
80 81 </body>
81 82 </html>
@@ -1,322 +1,340
1 1 /* redMine - project management software
2 2 Copyright (C) 2006-2008 Jean-Philippe Lang */
3 3
4 4 function checkAll (id, checked) {
5 5 var els = Element.descendants(id);
6 6 for (var i = 0; i < els.length; i++) {
7 7 if (els[i].disabled==false) {
8 8 els[i].checked = checked;
9 9 }
10 10 }
11 11 }
12 12
13 13 function toggleCheckboxesBySelector(selector) {
14 14 boxes = $$(selector);
15 15 var all_checked = true;
16 16 for (i = 0; i < boxes.length; i++) { if (boxes[i].checked == false) { all_checked = false; } }
17 17 for (i = 0; i < boxes.length; i++) { boxes[i].checked = !all_checked; }
18 18 }
19 19
20 20 function setCheckboxesBySelector(checked, selector) {
21 21 var boxes = $$(selector);
22 22 boxes.each(function(ele) {
23 23 ele.checked = checked;
24 24 });
25 25 }
26 26
27 27 function showAndScrollTo(id, focus) {
28 28 Element.show(id);
29 29 if (focus!=null) { Form.Element.focus(focus); }
30 30 Element.scrollTo(id);
31 31 }
32 32
33 33 function toggleRowGroup(el) {
34 34 var tr = Element.up(el, 'tr');
35 35 var n = Element.next(tr);
36 36 tr.toggleClassName('open');
37 37 while (n != undefined && !n.hasClassName('group')) {
38 38 Element.toggle(n);
39 39 n = Element.next(n);
40 40 }
41 41 }
42 42
43 43 function toggleFieldset(el) {
44 44 var fieldset = Element.up(el, 'fieldset');
45 45 fieldset.toggleClassName('collapsed');
46 46 Effect.toggle(fieldset.down('div'), 'slide', {duration:0.2});
47 47 }
48 48
49 49 function hideFieldset(el) {
50 50 var fieldset = Element.up(el, 'fieldset');
51 51 fieldset.toggleClassName('collapsed');
52 52 fieldset.down('div').hide();
53 53 }
54 54
55 55 var fileFieldCount = 1;
56 56
57 57 function addFileField() {
58 58 if (fileFieldCount >= 10) return false
59 59 fileFieldCount++;
60 60 var f = document.createElement("input");
61 61 f.type = "file";
62 62 f.name = "attachments[" + fileFieldCount + "][file]";
63 63 f.size = 30;
64 64 var d = document.createElement("input");
65 65 d.type = "text";
66 66 d.name = "attachments[" + fileFieldCount + "][description]";
67 67 d.size = 60;
68 68 var dLabel = new Element('label');
69 69 dLabel.addClassName('inline');
70 70 // Pulls the languge value used for Optional Description
71 71 dLabel.update($('attachment_description_label_content').innerHTML)
72 72 p = document.getElementById("attachments_fields");
73 73 p.appendChild(document.createElement("br"));
74 74 p.appendChild(f);
75 75 p.appendChild(dLabel);
76 76 dLabel.appendChild(d);
77 77
78 78 }
79 79
80 80 function showTab(name) {
81 81 var f = $$('div#content .tab-content');
82 82 for(var i=0; i<f.length; i++){
83 83 Element.hide(f[i]);
84 84 }
85 85 var f = $$('div.tabs a');
86 86 for(var i=0; i<f.length; i++){
87 87 Element.removeClassName(f[i], "selected");
88 88 }
89 89 Element.show('tab-content-' + name);
90 90 Element.addClassName('tab-' + name, "selected");
91 91 return false;
92 92 }
93 93
94 94 function moveTabRight(el) {
95 95 var lis = Element.up(el, 'div.tabs').down('ul').childElements();
96 96 var tabsWidth = 0;
97 97 var i;
98 98 for (i=0; i<lis.length; i++) {
99 99 if (lis[i].visible()) {
100 100 tabsWidth += lis[i].getWidth() + 6;
101 101 }
102 102 }
103 103 if (tabsWidth < Element.up(el, 'div.tabs').getWidth() - 60) {
104 104 return;
105 105 }
106 106 i=0;
107 107 while (i<lis.length && !lis[i].visible()) {
108 108 i++;
109 109 }
110 110 lis[i].hide();
111 111 }
112 112
113 113 function moveTabLeft(el) {
114 114 var lis = Element.up(el, 'div.tabs').down('ul').childElements();
115 115 var i = 0;
116 116 while (i<lis.length && !lis[i].visible()) {
117 117 i++;
118 118 }
119 119 if (i>0) {
120 120 lis[i-1].show();
121 121 }
122 122 }
123 123
124 124 function displayTabsButtons() {
125 125 var lis;
126 126 var tabsWidth = 0;
127 127 var i;
128 128 $$('div.tabs').each(function(el) {
129 129 lis = el.down('ul').childElements();
130 130 for (i=0; i<lis.length; i++) {
131 131 if (lis[i].visible()) {
132 132 tabsWidth += lis[i].getWidth() + 6;
133 133 }
134 134 }
135 135 if ((tabsWidth < el.getWidth() - 60) && (lis[0].visible())) {
136 136 el.down('div.tabs-buttons').hide();
137 137 } else {
138 138 el.down('div.tabs-buttons').show();
139 139 }
140 140 });
141 141 }
142 142
143 143 function setPredecessorFieldsVisibility() {
144 144 relationType = $('relation_relation_type');
145 145 if (relationType && (relationType.value == "precedes" || relationType.value == "follows")) {
146 146 Element.show('predecessor_fields');
147 147 } else {
148 148 Element.hide('predecessor_fields');
149 149 }
150 150 }
151 151
152 152 function promptToRemote(text, param, url) {
153 153 value = prompt(text + ':');
154 154 if (value) {
155 155 new Ajax.Request(url + '?' + param + '=' + encodeURIComponent(value), {asynchronous:true, evalScripts:true});
156 156 return false;
157 157 }
158 158 }
159 159
160 160 function collapseScmEntry(id) {
161 161 var els = document.getElementsByClassName(id, 'browser');
162 162 for (var i = 0; i < els.length; i++) {
163 163 if (els[i].hasClassName('open')) {
164 164 collapseScmEntry(els[i].id);
165 165 }
166 166 Element.hide(els[i]);
167 167 }
168 168 $(id).removeClassName('open');
169 169 }
170 170
171 171 function expandScmEntry(id) {
172 172 var els = document.getElementsByClassName(id, 'browser');
173 173 for (var i = 0; i < els.length; i++) {
174 174 Element.show(els[i]);
175 175 if (els[i].hasClassName('loaded') && !els[i].hasClassName('collapsed')) {
176 176 expandScmEntry(els[i].id);
177 177 }
178 178 }
179 179 $(id).addClassName('open');
180 180 }
181 181
182 182 function scmEntryClick(id) {
183 183 el = $(id);
184 184 if (el.hasClassName('open')) {
185 185 collapseScmEntry(id);
186 186 el.addClassName('collapsed');
187 187 return false;
188 188 } else if (el.hasClassName('loaded')) {
189 189 expandScmEntry(id);
190 190 el.removeClassName('collapsed');
191 191 return false;
192 192 }
193 193 if (el.hasClassName('loading')) {
194 194 return false;
195 195 }
196 196 el.addClassName('loading');
197 197 return true;
198 198 }
199 199
200 200 function scmEntryLoaded(id) {
201 201 Element.addClassName(id, 'open');
202 202 Element.addClassName(id, 'loaded');
203 203 Element.removeClassName(id, 'loading');
204 204 }
205 205
206 206 function randomKey(size) {
207 207 var chars = new Array('0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z');
208 208 var key = '';
209 209 for (i = 0; i < size; i++) {
210 210 key += chars[Math.floor(Math.random() * chars.length)];
211 211 }
212 212 return key;
213 213 }
214 214
215 215 function observeParentIssueField(url) {
216 216 new Ajax.Autocompleter('issue_parent_issue_id',
217 217 'parent_issue_candidates',
218 218 url,
219 219 { minChars: 3,
220 220 frequency: 0.5,
221 221 paramName: 'q',
222 222 updateElement: function(value) {
223 223 document.getElementById('issue_parent_issue_id').value = value.id;
224 224 }});
225 225 }
226 226
227 227 function observeRelatedIssueField(url) {
228 228 new Ajax.Autocompleter('relation_issue_to_id',
229 229 'related_issue_candidates',
230 230 url,
231 231 { minChars: 3,
232 232 frequency: 0.5,
233 233 paramName: 'q',
234 234 updateElement: function(value) {
235 235 document.getElementById('relation_issue_to_id').value = value.id;
236 236 },
237 237 parameters: 'scope=all'
238 238 });
239 239 }
240 240
241 241 function setVisible(id, visible) {
242 242 var el = $(id);
243 243 if (el) {if (visible) {el.show();} else {el.hide();}}
244 244 }
245 245
246 246 function observeProjectModules() {
247 247 var f = function() {
248 248 /* Hides trackers and issues custom fields on the new project form when issue_tracking module is disabled */
249 249 var c = ($('project_enabled_module_names_issue_tracking').checked == true);
250 250 setVisible('project_trackers', c);
251 251 setVisible('project_issue_custom_fields', c);
252 252 };
253 253
254 254 Event.observe(window, 'load', f);
255 255 Event.observe('project_enabled_module_names_issue_tracking', 'change', f);
256 256 }
257 257
258 258 /*
259 259 * Class used to warn user when leaving a page with unsaved textarea
260 260 * Author: mathias.fischer@berlinonline.de
261 261 */
262 262
263 263 var WarnLeavingUnsaved = Class.create({
264 264 observedForms: false,
265 265 observedElements: false,
266 266 changedForms: false,
267 267 message: null,
268 268
269 269 initialize: function(message){
270 270 this.observedForms = $$('form');
271 271 this.observedElements = $$('textarea');
272 272 this.message = message;
273 273
274 274 this.observedElements.each(this.observeChange.bind(this));
275 275 this.observedForms.each(this.submitAction.bind(this));
276 276
277 277 window.onbeforeunload = this.unload.bind(this);
278 278 },
279 279
280 280 unload: function(){
281 281 if(this.changedForms)
282 282 return this.message;
283 283 },
284 284
285 285 setChanged: function(){
286 286 this.changedForms = true;
287 287 },
288 288
289 289 setUnchanged: function(){
290 290 this.changedForms = false;
291 291 },
292 292
293 293 observeChange: function(element){
294 294 element.observe('change',this.setChanged.bindAsEventListener(this));
295 295 },
296 296
297 297 submitAction: function(element){
298 298 element.observe('submit',this.setUnchanged.bindAsEventListener(this));
299 299 }
300 300 });
301 301
302 /* shows and hides ajax indicator */
302 /*
303 * 1 - registers a callback which copies the csrf token into the
304 * X-CSRF-Token header with each ajax request. Necessary to
305 * work with rails applications which have fixed
306 * CVE-2011-0447
307 * 2 - shows and hides ajax indicator
308 */
303 309 Ajax.Responders.register({
304 onCreate: function(){
310 onCreate: function(request){
311 var csrf_meta_tag = $$('meta[name=csrf-token]')[0];
312
313 if (csrf_meta_tag) {
314 var header = 'X-CSRF-Token',
315 token = csrf_meta_tag.readAttribute('content');
316
317 if (!request.options.requestHeaders) {
318 request.options.requestHeaders = {};
319 }
320 request.options.requestHeaders[header] = token;
321 }
322
305 323 if ($('ajax-indicator') && Ajax.activeRequestCount > 0) {
306 324 Element.show('ajax-indicator');
307 325 }
308 326 },
309 327 onComplete: function(){
310 328 if ($('ajax-indicator') && Ajax.activeRequestCount == 0) {
311 329 Element.hide('ajax-indicator');
312 330 }
313 331 }
314 332 });
315 333
316 334 function hideOnLoad() {
317 335 $$('.hol').each(function(el) {
318 336 el.hide();
319 337 });
320 338 }
321 339
322 340 Event.observe(window, 'load', hideOnLoad);
General Comments 0
You need to be logged in to leave comments. Login now