facebook_rails 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +38 -0
- data/app/controllers/facebook_rails_controller.rb +191 -0
- data/app/helpers/facebook_rails_helper.rb +90 -0
- data/lib/facebook_rails/engine.rb +48 -0
- data/lib/facebook_rails/version.rb +3 -0
- data/lib/facebook_rails.rb +17 -0
- data/lib/generators/facebook_rails/facebook_rails_generator.rb +11 -0
- data/lib/generators/facebook_rails/templates/facebook.yml +29 -0
- data/lib/tasks/facebook_rails_tasks.rake +4 -0
- data/test/dummy/README.rdoc +261 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +15 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config/application.rb +56 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +37 -0
- data/test/dummy/config/environments/production.rb +67 -0
- data/test/dummy/config/environments/test.rb +37 -0
- data/test/dummy/config/facebook.yml +26 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +15 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +58 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/test.log +6 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +25 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/facebook_rails_test.rb +7 -0
- data/test/test_helper.rb +15 -0
- metadata +156 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright 2012 YOURNAME
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
data/Rakefile
ADDED
@@ -0,0 +1,38 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'FacebookRails'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
|
25
|
+
|
26
|
+
Bundler::GemHelper.install_tasks
|
27
|
+
|
28
|
+
require 'rake/testtask'
|
29
|
+
|
30
|
+
Rake::TestTask.new(:test) do |t|
|
31
|
+
t.libs << 'lib'
|
32
|
+
t.libs << 'test'
|
33
|
+
t.pattern = 'test/**/*_test.rb'
|
34
|
+
t.verbose = false
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
task :default => :test
|
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
|
3
|
+
module FacebookRailsController
|
4
|
+
def facebook_href(path = '/')
|
5
|
+
FACEBOOK['f8app_home'].to_s + path
|
6
|
+
end
|
7
|
+
|
8
|
+
def direct_href(path = '/')
|
9
|
+
FACEBOOK['f8app_callback'].to_s + path
|
10
|
+
end
|
11
|
+
|
12
|
+
def facebook_api
|
13
|
+
@facebook_api ||= Koala::Facebook::API.new(session[:access_token])
|
14
|
+
end
|
15
|
+
|
16
|
+
def current_user_data
|
17
|
+
@current_user_data ||= facebook_api.get_object("me")
|
18
|
+
return @current_user_data || {}
|
19
|
+
end
|
20
|
+
|
21
|
+
def current_user_f8id
|
22
|
+
session[:user_f8id] ||= current_user_data["id"]
|
23
|
+
end
|
24
|
+
|
25
|
+
def current_user_friends_f8ids
|
26
|
+
@current_user_friends_f8ids ||= facebook_api.get_connections('me','friends', :fields => 'id').collect { |f| f['id']}
|
27
|
+
rescue Exception => e
|
28
|
+
logger.error 'FACEBOOK ERROR: ' + e.to_s
|
29
|
+
return []
|
30
|
+
end
|
31
|
+
|
32
|
+
def check_if_f8ids_are_friends(first_f8id, second_f8id)
|
33
|
+
#handle corner cases with anonymous users
|
34
|
+
return false if first_f8id.nil? or second_f8id.nil?
|
35
|
+
|
36
|
+
return (facebook_api.fql_query('SELECT uid2 FROM friend where uid1=' + first_f8id + ' AND uid2=' + second_f8id).size > 0)
|
37
|
+
rescue Exception => e
|
38
|
+
logger.error "ERROR:" + e.to_s
|
39
|
+
return false
|
40
|
+
end
|
41
|
+
|
42
|
+
def url_smart_add_params(url, params = {})
|
43
|
+
new_url = url + (url.index(/\w\?\w/) ? '&' : '?')
|
44
|
+
params.each_pair {|key, value| new_url << key.to_s << "=" << value.to_s << '&'}
|
45
|
+
return new_url.chop
|
46
|
+
end
|
47
|
+
|
48
|
+
def clean_and_recode_current_url(escape = true)
|
49
|
+
omit_keys = ["_method", "format", "signed_request", "code", "clear_users_session"]
|
50
|
+
options = (params||{}).clone
|
51
|
+
options = options.reject{|k,v| omit_keys.include?(k.to_s)}
|
52
|
+
options = options.merge({:only_path => false})
|
53
|
+
if escape
|
54
|
+
return CGI.escape(url_for(options))
|
55
|
+
else
|
56
|
+
return url_for(options)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def top_redirect_to(url)
|
61
|
+
@redirect_url = CGI.unescape(url)
|
62
|
+
render :layout => false, :inline => <<-HTML
|
63
|
+
<html><head>
|
64
|
+
<script type="text/javascript">
|
65
|
+
window.top.location.href = "<%=raw @redirect_url -%>";
|
66
|
+
</script>
|
67
|
+
<noscript>
|
68
|
+
<meta http-equiv="refresh" content="0;url=<%= @redirect_url %>" />
|
69
|
+
<meta http-equiv="window-target" content="_top" />
|
70
|
+
</noscript>
|
71
|
+
</head></html>
|
72
|
+
HTML
|
73
|
+
end
|
74
|
+
|
75
|
+
def authenticated_by_facebook?
|
76
|
+
return !session[:access_token].nil?
|
77
|
+
end
|
78
|
+
|
79
|
+
#Will only try to collect facebook credentials but it will let the user in even there aren't any
|
80
|
+
def try_facebook_authentication
|
81
|
+
attempt_to_acquire_facebook_credentials do |fb_oauth|
|
82
|
+
clear_oauth
|
83
|
+
end
|
84
|
+
rescue Exception => e
|
85
|
+
clear_oauth
|
86
|
+
logger.error "FACEBOOK AUTH EXCEPTION: #{ e.inspect } #{ e.backtrace }"
|
87
|
+
end
|
88
|
+
|
89
|
+
#Will force user to authorize the application to access the current url
|
90
|
+
def ensure_authenticated_to_facebook
|
91
|
+
attempt_to_acquire_facebook_credentials do |fb_oauth|
|
92
|
+
if params[:error_reason] == "user_denied"
|
93
|
+
redirect_url = root_url
|
94
|
+
else
|
95
|
+
redirect_url = fb_oauth.url_for_oauth_code(:callback => clean_and_recode_current_url, :permissions => Game.extended_permissions)
|
96
|
+
end
|
97
|
+
top_redirect_to(redirect_url)
|
98
|
+
end
|
99
|
+
rescue Exception => e
|
100
|
+
logger.error 'AUTHENTICATION ERROR: ' + e.to_s
|
101
|
+
top_redirect_to clean_and_recode_current_url(false)
|
102
|
+
end
|
103
|
+
|
104
|
+
#prepend before filter to force authentication when collect_facebook_authentication is called
|
105
|
+
def require_facebook_authentication
|
106
|
+
@require_fb_authentication = true
|
107
|
+
end
|
108
|
+
|
109
|
+
#Add method to before filter to collect user
|
110
|
+
def collect_facebook_authentication
|
111
|
+
if @require_fb_authentication
|
112
|
+
ensure_authenticated_to_facebook
|
113
|
+
else
|
114
|
+
try_facebook_authentication
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def get_facebook_locale
|
119
|
+
puts current_user_data.inspect
|
120
|
+
current_user_data["locale"]
|
121
|
+
rescue Exception => e
|
122
|
+
logger.warn "Facebook locale error: #{ e.inspect }"
|
123
|
+
session[:f8_locale] || "en_US"
|
124
|
+
end
|
125
|
+
|
126
|
+
private
|
127
|
+
#Tries to extract facebook credentials, if it thinks a redirect is needed it yields to the block
|
128
|
+
#and returns false else it returns true
|
129
|
+
#Will let exceptions propagate
|
130
|
+
def attempt_to_acquire_facebook_credentials
|
131
|
+
if params[:clear_users_session]
|
132
|
+
clear_oauth
|
133
|
+
logger.warn "User's session cleared"
|
134
|
+
end
|
135
|
+
|
136
|
+
#First check we received the call with a signed_request parameter, as that means a fresh token
|
137
|
+
if params[:signed_request]
|
138
|
+
fb_oauth = Koala::Facebook::OAuth.new
|
139
|
+
f8_params = fb_oauth.parse_signed_request(params.delete(:signed_request))
|
140
|
+
session[:f8_locale] = (f8_params and f8_params["user"]) ? f8_params["user"]["locale"] : nil
|
141
|
+
#puts "request params: #{ f8_params.inspect }"
|
142
|
+
if f8_params["oauth_token"]
|
143
|
+
set_oauth f8_params["oauth_token"], f8_params["expires"].to_i, f8_params["user_id"]
|
144
|
+
else
|
145
|
+
yield(fb_oauth)
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
|
149
|
+
#Then check if received a code because we just came from authorization, that also means a fresh token
|
150
|
+
elsif params[:code]
|
151
|
+
fb_oauth = Koala::Facebook::OAuth.new(clean_and_recode_current_url(false))
|
152
|
+
oauth_token_info = fb_oauth.get_access_token_info(params.delete(:code))
|
153
|
+
#puts "code params: #{ oauth_token_info.inspect }"
|
154
|
+
set_oauth oauth_token_info['access_token'], Time.now.to_i + oauth_token_info['expires'].to_i, nil
|
155
|
+
|
156
|
+
else
|
157
|
+
#Try to parse cookies
|
158
|
+
fb_oauth = Koala::Facebook::OAuth.new
|
159
|
+
f8_params = fb_oauth.get_user_info_from_cookies(cookies)
|
160
|
+
#puts "cookie params: #{ f8_params.inspect }"
|
161
|
+
if f8_params and f8_params["access_token"]
|
162
|
+
set_oauth f8_params["access_token"], Time.now.to_i + f8_params["expires"].to_i, f8_params["user_id"]
|
163
|
+
|
164
|
+
#Then we check if have a valid token stored in the session
|
165
|
+
#puts "session: #{ session.inspect }"
|
166
|
+
elsif session[:access_token] and session[:expires] > Time.now.to_i
|
167
|
+
#Do nothing
|
168
|
+
|
169
|
+
#if all else fails (we lost the session or something went wrong in the request sending) redirect the authorization
|
170
|
+
#screen. If the user has already authorized the application he will pass straight through without noticing and we
|
171
|
+
#will receive a fresh token via the code parameter
|
172
|
+
else
|
173
|
+
yield(fb_oauth)
|
174
|
+
return false
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
return true
|
179
|
+
end
|
180
|
+
|
181
|
+
def set_oauth(access_token = nil, expires = nil, user_f8id = nil)
|
182
|
+
session[:access_token] = access_token
|
183
|
+
session[:expires] = expires
|
184
|
+
session[:user_f8id] = user_f8id
|
185
|
+
@facebook_api = nil
|
186
|
+
end
|
187
|
+
|
188
|
+
def clear_oauth
|
189
|
+
set_oauth nil, nil, nil
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,90 @@
|
|
1
|
+
module FacebookRailsHelper
|
2
|
+
def reload_back_to_facebook_if_needed_js
|
3
|
+
return '<script>' +
|
4
|
+
'if (top == self)' +
|
5
|
+
'{' +
|
6
|
+
"top.location = '#{facebook_href(url_for( { :controller => controller.controller_name, :action => controller.action_name}.merge params.reject {|k,v| k == 'signed_request'}))}';" +
|
7
|
+
'}' +
|
8
|
+
'</script>'
|
9
|
+
end
|
10
|
+
|
11
|
+
def facebook_initialization one_time_resize = false, app_id = FACEBOOK['app_id'], user_f8id = current_user_f8id
|
12
|
+
resize = %{
|
13
|
+
var domLoaded = false;
|
14
|
+
var facebookLoaded = false;
|
15
|
+
var pageLoaded = false;
|
16
|
+
|
17
|
+
function pageLoad() {
|
18
|
+
if (domLoaded && facebookLoaded) {
|
19
|
+
if (!pageLoaded) {
|
20
|
+
pageLoaded = true;
|
21
|
+
$(document).trigger('page_loaded');
|
22
|
+
}
|
23
|
+
sizeToMain();
|
24
|
+
}
|
25
|
+
}
|
26
|
+
|
27
|
+
function sizeToMain() {
|
28
|
+
if (domLoaded && facebookLoaded) {
|
29
|
+
var height = $('#main').outerHeight();
|
30
|
+
if(height > 100) {
|
31
|
+
FB.Canvas.setSize({ width: 760, height: height + 200 });
|
32
|
+
}
|
33
|
+
}
|
34
|
+
}
|
35
|
+
|
36
|
+
$(document).ready(function () {
|
37
|
+
domLoaded = true;
|
38
|
+
pageLoad();
|
39
|
+
});
|
40
|
+
$(window).load(function() {
|
41
|
+
domLoaded = true; //not neccessary
|
42
|
+
pageLoad();
|
43
|
+
});
|
44
|
+
}
|
45
|
+
|
46
|
+
login_status = if user_f8id
|
47
|
+
%{
|
48
|
+
FB.getLoginStatus(function(response) {
|
49
|
+
if ((response.authResponse && response.authResponse.userID == '#{ user_f8id }') ||
|
50
|
+
(response.session && response.session.uid == '#{ user_f8id }')) {
|
51
|
+
// logged in and connected user - continue
|
52
|
+
$(document).trigger('got_facebook_status');
|
53
|
+
} else {
|
54
|
+
// no user session available or new user - clear old user's session
|
55
|
+
window.location.href = window.location.href + (window.location.href.indexOf('?') >= 0 ? '&' : '?') + 'clear_users_session=true';
|
56
|
+
}
|
57
|
+
}, true); //force session status reloading
|
58
|
+
}
|
59
|
+
else
|
60
|
+
%{
|
61
|
+
FB.getLoginStatus(function(response) {
|
62
|
+
$(document).trigger('got_facebook_status');
|
63
|
+
});
|
64
|
+
}
|
65
|
+
end
|
66
|
+
|
67
|
+
%{
|
68
|
+
<div id="fb-root"></div>
|
69
|
+
<script>
|
70
|
+
#{ resize if one_time_resize }
|
71
|
+
window.fbAsyncInit = function() {
|
72
|
+
FB.init({ appId: '#{ app_id }',
|
73
|
+
status: true,
|
74
|
+
cookie: true,
|
75
|
+
xfbml: true });
|
76
|
+
$(document).trigger('facebook_init');
|
77
|
+
#{ one_time_resize ? 'facebookLoaded = true; pageLoad();' : 'FB.Canvas.setAutoGrow();' }
|
78
|
+
#{ login_status }
|
79
|
+
};
|
80
|
+
|
81
|
+
(function() {
|
82
|
+
var e = document.createElement('script'); e.async = true;
|
83
|
+
e.src = document.location.protocol +
|
84
|
+
'//connect.facebook.net/#{ get_facebook_locale }/all.js';
|
85
|
+
document.getElementById('fb-root').appendChild(e);
|
86
|
+
}());
|
87
|
+
</script>
|
88
|
+
}.html_safe
|
89
|
+
end
|
90
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'koala'
|
2
|
+
|
3
|
+
module FacebookRails
|
4
|
+
|
5
|
+
class Engine < Rails::Engine
|
6
|
+
|
7
|
+
initializer "facebook_rails.load_app_instance_data" do |app|
|
8
|
+
FacebookRails.setup do |config|
|
9
|
+
config.app_root = app.root
|
10
|
+
config.app_env = Rails.env
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
initializer "facebook_rails.facebook" do
|
15
|
+
::FACEBOOK = YAML.load_file(FacebookRails.app_root.join("config/facebook.yml"))[Rails.env]
|
16
|
+
FACEBOOK.default = ''
|
17
|
+
|
18
|
+
::Koala.http_service.http_options[:timeout] = FACEBOOK[:timeout].to_i unless FACEBOOK[:timeout].blank?
|
19
|
+
|
20
|
+
::Koala::Facebook::OAuth.class_eval do
|
21
|
+
def initialize_with_default_settings(*args)
|
22
|
+
case args.size
|
23
|
+
when 0, 1
|
24
|
+
raise "application id and/or secret are not specified in the config" unless FACEBOOK['app_id'] && FACEBOOK['secret_key']
|
25
|
+
initialize_without_default_settings(FACEBOOK['app_id'].to_s, FACEBOOK['secret_key'].to_s, args.first)
|
26
|
+
when 2, 3
|
27
|
+
initialize_without_default_settings(*args)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
alias_method_chain :initialize, :default_settings
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
initializer "facebook_rails.application_controller" do
|
36
|
+
ActionController::Base.send :include, FacebookRailsController
|
37
|
+
|
38
|
+
ActionController::Base.send :helper_method, :facebook_href
|
39
|
+
ActionController::Base.send :helper_method, :direct_href
|
40
|
+
ActionController::Base.send :helper_method, :current_user_f8id
|
41
|
+
ActionController::Base.send :helper_method, :get_facebook_locale
|
42
|
+
end
|
43
|
+
|
44
|
+
initializer "facebook_rails.application_helper" do
|
45
|
+
ActionView::Base.send :include, FacebookRailsHelper
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require "active_support/dependencies"
|
2
|
+
|
3
|
+
module FacebookRails
|
4
|
+
|
5
|
+
# Our host application root path
|
6
|
+
# We set this when the engine is initialized
|
7
|
+
mattr_accessor :app_root
|
8
|
+
mattr_accessor :app_env
|
9
|
+
|
10
|
+
# Yield self on setup for nice config blocks
|
11
|
+
def self.setup
|
12
|
+
yield self
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
require 'facebook_rails/engine'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'rails/generators'
|
2
|
+
|
3
|
+
class FacebookRailsGenerator < Rails::Generators::Base
|
4
|
+
def self.source_root
|
5
|
+
@source_root ||= File.expand_path(File.join(File.dirname(__FILE__), 'templates'))
|
6
|
+
end
|
7
|
+
|
8
|
+
def copy_configuration
|
9
|
+
template 'facebook.yml', 'config/facebook.yml'
|
10
|
+
end
|
11
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
#TODO Go to https://developers.facebook.com/apps, create a new App and enter the correct values here
|
2
|
+
|
3
|
+
# config/facebook.yml
|
4
|
+
development:
|
5
|
+
app_id: YOUR_APP_ID
|
6
|
+
secret_key: YOUR_SECRET
|
7
|
+
#Slijede drap dodaci
|
8
|
+
f8app_home: http://apps.facebook.com/CANVAS_NAME #bez zadnjeg '/'
|
9
|
+
f8app_callback: CALLBACK_URL #bez zadnjeg '/'
|
10
|
+
#server_subdir:
|
11
|
+
timeout: 3
|
12
|
+
|
13
|
+
test:
|
14
|
+
app_id: YOUR_APP_ID
|
15
|
+
secret_key: YOUR_SECRET
|
16
|
+
#Slijede drap dodaci
|
17
|
+
f8app_home: CANVAS_URL #bez zadnjeg '/'
|
18
|
+
f8app_callback: CALLBACK_URL #bez zadnjeg '/'
|
19
|
+
#server_subdir:
|
20
|
+
timeout: 3
|
21
|
+
|
22
|
+
production:
|
23
|
+
app_id: YOUR_APP_ID
|
24
|
+
secret_key: YOUR_SECRET
|
25
|
+
#Slijede drap dodaci
|
26
|
+
f8app_home: CANVAS_URL #bez zadnjeg '/'
|
27
|
+
f8app_callback: CALLBACK_URL #bez zadnjeg '/'
|
28
|
+
#server_subdir:
|
29
|
+
timeout: 3
|