togo 0.6.2 → 0.6.3
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +108 -7
- data/lib/togo/admin/admin.rb +1 -1
- data/lib/togo/admin/helpers.rb +12 -0
- data/lib/togo/admin/public/css/login.css +4 -4
- data/lib/togo/admin/public/css/screen.css +12 -12
- data/lib/togo/admin/public/js/togo.js +24 -5
- data/lib/togo/admin/views/edit.erb +4 -4
- data/lib/togo/admin/views/index.erb +5 -5
- data/lib/togo/admin/views/layout.erb +8 -3
- data/lib/togo/admin/views/login.erb +5 -5
- data/lib/togo/admin/views/new.erb +4 -4
- data/lib/togo/dispatch/dispatch.rb +37 -2
- data/lib/togo/model/property_wrapper/property_wrapper.rb +0 -1
- data/lib/togo/model/types/belongs_to.erb +2 -1
- data/lib/togo/model/types/has_n.erb +2 -1
- data/lib/togo/model/types/many_to_many.erb +2 -1
- metadata +4 -4
data/README.md
CHANGED
@@ -1,12 +1,17 @@
|
|
1
1
|
# Introducing Togo
|
2
2
|
|
3
|
-
Togo is a CMS framework.
|
4
|
-
|
3
|
+
Togo is a CMS framework. The goal of Togo is to be a modular system that can be used with any Ruby ORM framework, giving you a complete CMS right out of the box.
|
4
|
+
It is built to be as independent as possible from other web frameworks such as Rails or Sinatra, and only needs Rack and your choice of HTTP server to run (such as thin, mongrel or webrick).
|
5
|
+
Togo is focused only on making a quick and easy way to manage content, and is less about integrating into your existing web application.
|
5
6
|
|
6
7
|
## Installation
|
7
8
|
|
8
9
|
gem install togo
|
9
10
|
|
11
|
+
## Example Application
|
12
|
+
|
13
|
+
An example application is available: [http://github.com/mattking17/Togo-Example-App](http://github.com/mattking17/Togo-Example-App)
|
14
|
+
|
10
15
|
## Tutorial
|
11
16
|
|
12
17
|
Togo works by a simply including a line in your model definition (currently, Togo only works for DataMapper). By including Togo in your model,
|
@@ -20,7 +25,7 @@ gives a good idea of how Togo can be used in conjuction with an existing project
|
|
20
25
|
|
21
26
|
You'll need the following gems installed:
|
22
27
|
|
23
|
-
|
28
|
+
dm-core dm-serializer sinatra dm-sqlite3-adapter
|
24
29
|
|
25
30
|
Next, make a directory for your project like so:
|
26
31
|
|
@@ -105,8 +110,8 @@ Open up http://0.0.0.0:8080 in your browser and have a blast! Have a look at our
|
|
105
110
|
|
106
111
|
## Customizing Togo
|
107
112
|
|
108
|
-
|
109
|
-
use your own templates
|
113
|
+
Togo is set up to work with sensible defaults out of the box, but you can customize what fields are used, what order they appear, and even
|
114
|
+
use your own templates to show fields when editing or creating content.
|
110
115
|
|
111
116
|
### Telling Togo which fields to use
|
112
117
|
|
@@ -131,7 +136,7 @@ If you only want the title and date field to appear in the list view of blog ent
|
|
131
136
|
|
132
137
|
Togo will display the fields in the order given. So if you wanted date first some reason, you could just do
|
133
138
|
|
134
|
-
|
139
|
+
list_properties :date, :title
|
135
140
|
|
136
141
|
And date will be listed in the first column.
|
137
142
|
|
@@ -166,6 +171,24 @@ Associations also can be used in list and form property declarations:
|
|
166
171
|
|
167
172
|
end
|
168
173
|
|
174
|
+
Togo also allows for displaying values returned from instance methods on your model.
|
175
|
+
This opens up customization of the list display even further.
|
176
|
+
|
177
|
+
class BlogEntry
|
178
|
+
|
179
|
+
include DataMapper::Resource
|
180
|
+
include Togo::DataMapper::Model
|
181
|
+
... snipped ...
|
182
|
+
|
183
|
+
list_properties :title, :date, :number_of_comments
|
184
|
+
form_properties :title, :date, :body, :comments
|
185
|
+
|
186
|
+
def number_of_comments
|
187
|
+
comments.count
|
188
|
+
end
|
189
|
+
|
190
|
+
end
|
191
|
+
|
169
192
|
|
170
193
|
#### Configuring properties
|
171
194
|
|
@@ -207,4 +230,82 @@ Each property type has it's own default form template that can be overridden sim
|
|
207
230
|
|
208
231
|
Note we used the SITE_ROOT constant we defined in the init.rb file, how you get the full path to your template may vary depending on your setup.
|
209
232
|
|
210
|
-
See the Writing a Form Template Guide for more information. (Coming Soon)
|
233
|
+
See the Writing a Form Template Guide for more information. (Coming Soon)
|
234
|
+
|
235
|
+
|
236
|
+
## Configuring Togo::Admin
|
237
|
+
|
238
|
+
There are currently only a couple configuration options for Togo Admin which affect runtime, but you can also pass in any
|
239
|
+
arbitrary configuration parameters and access them through custom templates in a global config hash. In our example, let's open
|
240
|
+
init.rb and configure Togo::Admin:
|
241
|
+
|
242
|
+
#init.rb:
|
243
|
+
|
244
|
+
SITE_ROOT = File.dirname(File.expand_path(__FILE__))
|
245
|
+
... snipped ...
|
246
|
+
|
247
|
+
Togo::Admin.configure({:site_title => "My Admin Title", :my_custom_config => "Custom Config Value"})
|
248
|
+
|
249
|
+
|
250
|
+
If you had a custom template for your property and wanted to access :my_custom_config:
|
251
|
+
|
252
|
+
<%= config[:my_custom_config] %>
|
253
|
+
|
254
|
+
|
255
|
+
### Authentication
|
256
|
+
|
257
|
+
By default Togo is not protected by an authentication method. If you're going to run Togo on a public web server, you'll
|
258
|
+
most likely want to protect it. Luckily you can tell Togo to use any object you desire to autenticate against.
|
259
|
+
|
260
|
+
First, define any object that responds to two methods:
|
261
|
+
|
262
|
+
#### self.authenticate(username, password)
|
263
|
+
|
264
|
+
A class method that Togo Admin will call, passing in a username and password. If successful, You must pass back an instance
|
265
|
+
of your object with a property called authenticated? set to true. If authentication fails, you can still pass back an
|
266
|
+
instance of your object, but with authenticated? returning false, or can return nil.
|
267
|
+
|
268
|
+
#### authenticated?
|
269
|
+
|
270
|
+
What Togo Admin will ask an instance of your object before every request. Must return true or false.
|
271
|
+
|
272
|
+
If the Togo Admin authenticates the user successfully, it will store the instance of that object in the session and check it
|
273
|
+
before each request to see if the user is authenticated or not.
|
274
|
+
|
275
|
+
### Example User object for authentication
|
276
|
+
|
277
|
+
Here is an trivial example using a DataMapper object to authenticate a user:
|
278
|
+
|
279
|
+
|
280
|
+
class User
|
281
|
+
|
282
|
+
include DataMapper::Resource
|
283
|
+
property :id, Serial
|
284
|
+
property :username, String
|
285
|
+
property :password, String
|
286
|
+
|
287
|
+
attr_accessor :authenticated
|
288
|
+
|
289
|
+
def authenticated?
|
290
|
+
authenticated || false
|
291
|
+
end
|
292
|
+
|
293
|
+
def self.authenticate(u, p)
|
294
|
+
u = first(:username => u, :password => p)
|
295
|
+
u.authenticated = true if u
|
296
|
+
u
|
297
|
+
end
|
298
|
+
|
299
|
+
end
|
300
|
+
|
301
|
+
|
302
|
+
Note that the object does *not* have to have the Togo Model module included to work, unless you want it to.
|
303
|
+
|
304
|
+
### Enabling Authentication
|
305
|
+
|
306
|
+
Finally tell Togo Admin to authenticate against your User object by configuring the Togo::Admin
|
307
|
+
application at runtime, either in your init.rb or togo-admin-config.rb file, like so:
|
308
|
+
|
309
|
+
Togo::Admin.configure({:auth_model => User})
|
310
|
+
|
311
|
+
It's up to you how to to authenticate the user: Local Database, LDAP, Kerberos, etc.
|
data/lib/togo/admin/admin.rb
CHANGED
data/lib/togo/admin/helpers.rb
CHANGED
@@ -42,4 +42,16 @@ module Helpers
|
|
42
42
|
config[:auth_model] and session[:user] and session[:user].authenticated?
|
43
43
|
end
|
44
44
|
|
45
|
+
def admin_url(url)
|
46
|
+
[@path_prefix, url].join
|
47
|
+
end
|
48
|
+
|
49
|
+
def return_url
|
50
|
+
if request.referer
|
51
|
+
request.referer.gsub(/^http:\/\/.*?\/(.*?)$/,"/\\1").gsub(Regexp.new("^#{@path_prefix}"), '')
|
52
|
+
else
|
53
|
+
admin_url('/' + @model.name)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
45
57
|
end
|
@@ -15,7 +15,7 @@ h3.error {
|
|
15
15
|
font-size: 14px;
|
16
16
|
}
|
17
17
|
body {
|
18
|
-
background: #efefef url(
|
18
|
+
background: #efefef url(../img/login-bg.png) top left repeat-x;
|
19
19
|
font: 100% helvetica, arial, sans-serif;
|
20
20
|
margin: 0;
|
21
21
|
padding: 0;
|
@@ -29,7 +29,7 @@ body {
|
|
29
29
|
-moz-border-radius: 4px;
|
30
30
|
}
|
31
31
|
#container h1 {
|
32
|
-
background: #586069 url(
|
32
|
+
background: #586069 url(../img/bg-header.png) repeat-x top left;
|
33
33
|
font-size: 24px;
|
34
34
|
color: #FFF;
|
35
35
|
text-shadow: 1px 1px 1px #5e6a76;
|
@@ -75,7 +75,7 @@ body {
|
|
75
75
|
}
|
76
76
|
#container form button {
|
77
77
|
border: 1px solid #549300;
|
78
|
-
background: #61a801 url(
|
78
|
+
background: #61a801 url(../img/button-bg-save.png) repeat-x top left;
|
79
79
|
-webkit-border-radius: 4px;
|
80
80
|
-moz-border-radius: 4px;
|
81
81
|
padding: 5px 10px;
|
@@ -85,7 +85,7 @@ body {
|
|
85
85
|
color: #FFF;
|
86
86
|
}
|
87
87
|
#container form button:hover {
|
88
|
-
background: #69b802 url(
|
88
|
+
background: #69b802 url(../img/button-bg-save.png) repeat-x -30px left;
|
89
89
|
text-shadow: 0 1px 1px #538A00;
|
90
90
|
cursor: pointer;
|
91
91
|
}
|
@@ -8,7 +8,7 @@ body {
|
|
8
8
|
padding: 0;
|
9
9
|
}
|
10
10
|
.error {
|
11
|
-
background: #800000 url(
|
11
|
+
background: #800000 url(../img/button-bg-delete.png) repeat-x top left;
|
12
12
|
text-shadow: 0 1px 1px #710000;
|
13
13
|
border: 1px solid #a12f19;
|
14
14
|
-webkit-border-radius: 2px;
|
@@ -23,7 +23,7 @@ h3.error {
|
|
23
23
|
background: #f5f5f5;
|
24
24
|
}
|
25
25
|
#header {
|
26
|
-
background: #586069 url(
|
26
|
+
background: #586069 url(../img/bg-header.png) repeat-x top left;
|
27
27
|
}
|
28
28
|
#header h1 {
|
29
29
|
font-size: 24px;
|
@@ -110,7 +110,7 @@ h1:hover > #model-select {
|
|
110
110
|
-webkit-box-shadow: -2px 0 20px rgba(51,51,51,0.3);
|
111
111
|
}
|
112
112
|
#content #subhead {
|
113
|
-
background: #e0e0e0 url(
|
113
|
+
background: #e0e0e0 url(../img/subhead-bg.png) repeat-x top left;
|
114
114
|
border-bottom: 1px solid #d4d2d2;
|
115
115
|
padding-left: 20px;
|
116
116
|
}
|
@@ -120,7 +120,7 @@ h1:hover > #model-select {
|
|
120
120
|
line-height: 44px;
|
121
121
|
margin: 0;
|
122
122
|
color: #5e6a76;
|
123
|
-
background-image: url(
|
123
|
+
background-image: url(../img/arrow-down.png);
|
124
124
|
background-position: 91% 50%;
|
125
125
|
background-repeat: no-repeat;
|
126
126
|
border-right: 1px solid #d4d2d2;
|
@@ -170,10 +170,10 @@ h1:hover > #model-select {
|
|
170
170
|
padding-right: 15px;
|
171
171
|
}
|
172
172
|
#content table th a.desc {
|
173
|
-
background: transparent url(
|
173
|
+
background: transparent url(../img/arrows.png) no-repeat 2px right;
|
174
174
|
}
|
175
175
|
#content table th a.asc {
|
176
|
-
background: transparent url(
|
176
|
+
background: transparent url(../img/arrows.png) no-repeat right -16px;
|
177
177
|
}
|
178
178
|
#content table td {
|
179
179
|
padding: 10px;
|
@@ -196,7 +196,7 @@ h1:hover > #model-select {
|
|
196
196
|
}
|
197
197
|
#content table tr.disabled td {
|
198
198
|
color: #888;
|
199
|
-
background: transparent url(
|
199
|
+
background: transparent url(../img/disabled.png);
|
200
200
|
}
|
201
201
|
#content table tr.disabled td span.address,
|
202
202
|
#content table tr.disabled td a {
|
@@ -208,7 +208,7 @@ h1:hover > #model-select {
|
|
208
208
|
width: 20px;
|
209
209
|
}
|
210
210
|
#content div.actions {
|
211
|
-
background: #ccc url(
|
211
|
+
background: #ccc url(../img/footer-bg.png) repeat-x top left;
|
212
212
|
position: absolute;
|
213
213
|
left: 0;
|
214
214
|
right: 0;
|
@@ -221,7 +221,7 @@ h1:hover > #model-select {
|
|
221
221
|
}
|
222
222
|
#content div.actions button {
|
223
223
|
border: 1px solid #549300;
|
224
|
-
background: #61a801 url(
|
224
|
+
background: #61a801 url(../img/button-bg-save.png) repeat-x top left;
|
225
225
|
-webkit-border-radius: 4px;
|
226
226
|
-moz-border-radius: 4px;
|
227
227
|
padding: 5px 10px;
|
@@ -231,12 +231,12 @@ h1:hover > #model-select {
|
|
231
231
|
color: #FFF;
|
232
232
|
}
|
233
233
|
#content div.actions button:hover {
|
234
|
-
background: #69b802 url(
|
234
|
+
background: #69b802 url(../img/button-bg-save.png) repeat-x -30px left;
|
235
235
|
text-shadow: 0 1px 1px #538A00;
|
236
236
|
cursor: pointer;
|
237
237
|
}
|
238
238
|
#content div.actions button#delete-button {
|
239
|
-
background: #800000 url(
|
239
|
+
background: #800000 url(../img/button-bg-delete.png) repeat-x top left;
|
240
240
|
text-shadow: 0 1px 1px #710000;
|
241
241
|
border: 1px solid #a12f19;
|
242
242
|
}
|
@@ -247,7 +247,7 @@ h1:hover > #model-select {
|
|
247
247
|
text-shadow: none;
|
248
248
|
}
|
249
249
|
#content div.actions button#delete-button:hover {
|
250
|
-
background: #910000 url(
|
250
|
+
background: #910000 url(../img/button-bg-delete.png) repeat-x -30px left;
|
251
251
|
text-shadow: 0 1px 1px #A12F19;
|
252
252
|
}
|
253
253
|
#content div.actions button#delete-button:disabled:hover {
|
@@ -179,6 +179,7 @@ var AssociationManager = function(opts) {
|
|
179
179
|
this.pageCount = Math.ceil(this.count/10);
|
180
180
|
this.pageNumber = 1;
|
181
181
|
this.relatedIds = {};
|
182
|
+
this.pathPrefix = opts.pathPrefix;
|
182
183
|
|
183
184
|
for (var i = 0, len = opts.related_ids.length; i < len; i++) {
|
184
185
|
this.relatedIds[opts.related_ids[i]] = true;
|
@@ -238,7 +239,7 @@ AssociationManager.prototype.adjustRelatedList = function() {
|
|
238
239
|
getElementsByClassName('related_ids', this.fieldset)[0].value = selected.join(',');
|
239
240
|
var handler = this;
|
240
241
|
ajax({
|
241
|
-
url: "/search/" + this.model + "?ids=" + selected.slice(0,10).join(','),
|
242
|
+
url: (this.pathPrefix + "/search/" + this.model + "?ids=" + selected.slice(0,10).join(',')),
|
242
243
|
success: function(data) {
|
243
244
|
handler.searchResultsHandler(handler)(data);
|
244
245
|
handler.count = selected.length;
|
@@ -255,7 +256,7 @@ AssociationManager.prototype.adjustRelatedList = function() {
|
|
255
256
|
|
256
257
|
AssociationManager.prototype.setToModifyState = function() {
|
257
258
|
ajax({
|
258
|
-
url: "/search/" + this.model,
|
259
|
+
url: (this.pathPrefix + "/search/" + this.model),
|
259
260
|
success: this.searchResultsHandler(this)
|
260
261
|
});
|
261
262
|
};
|
@@ -310,7 +311,7 @@ AssociationManager.prototype.pagingHandler = function(handler) {
|
|
310
311
|
handler.pageNumberField.innerHTML = handler.pageNumber;
|
311
312
|
var offset = (handler.pageNumber-1)*10;
|
312
313
|
ajax({
|
313
|
-
url: "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset,
|
314
|
+
url: (handler.pathPrefix + "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset),
|
314
315
|
success: handler.searchResultsHandler(handler)
|
315
316
|
});
|
316
317
|
}
|
@@ -382,7 +383,7 @@ AssociationManager.prototype.searchHandler = function(handler) {
|
|
382
383
|
handler.pageNumber = 1;
|
383
384
|
var offset = (handler.pageNumber-1)*10;
|
384
385
|
ajax({
|
385
|
-
url: "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset,
|
386
|
+
url: (handler.pathPrefix + "/search/" + handler.model + "?q=" + q + "&limit=10&offset=" + offset),
|
386
387
|
success: handler.searchResultsHandler(handler)
|
387
388
|
});
|
388
389
|
return false;
|
@@ -400,4 +401,22 @@ AssociationManager.prototype.searchInputHandler = function(handler, action) {
|
|
400
401
|
this.value = '';
|
401
402
|
}
|
402
403
|
};
|
403
|
-
};
|
404
|
+
};
|
405
|
+
|
406
|
+
var Togo = (function() {
|
407
|
+
|
408
|
+
var config = {};
|
409
|
+
|
410
|
+
var configure = function(opts) {
|
411
|
+
for (k in opts) {
|
412
|
+
config[k] = opts[k];
|
413
|
+
}
|
414
|
+
};
|
415
|
+
|
416
|
+
return {
|
417
|
+
config: config,
|
418
|
+
configure: configure
|
419
|
+
};
|
420
|
+
|
421
|
+
}());
|
422
|
+
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<form action="/update
|
2
|
-
<input type="hidden" name="return_url" value="<%=
|
1
|
+
<form action="<%= admin_url('/update/' + @model.name + '/' + @content.id.to_s) %>" method="post" id="edit-form">
|
2
|
+
<input type="hidden" name="return_url" value="<%= return_url %>" />
|
3
3
|
<div id="subhead">
|
4
|
-
<h1><a href="
|
4
|
+
<h1><a href="<%= admin_url('/' + @model.name) %>"><%= @model.display_name %></a> / Edit</h1>
|
5
5
|
</div>
|
6
6
|
<div id="main">
|
7
7
|
<% if @errors %><div id="save_errors" class="errors"><%= @errors %></div><% end %>
|
@@ -15,4 +15,4 @@
|
|
15
15
|
<button type="submit" class="save">Save <%= @model.display_name.singularize %></button>
|
16
16
|
</div>
|
17
17
|
</form>
|
18
|
-
<script type="text/javascript" src="/js/edit.js"></script>
|
18
|
+
<script type="text/javascript" src="<%= admin_url('/js/edit.js') %>"></script>
|
@@ -4,7 +4,7 @@
|
|
4
4
|
<ul>
|
5
5
|
<% Togo.models.each do |m| %>
|
6
6
|
<% next if m == @model %>
|
7
|
-
<li><a href="
|
7
|
+
<li><a href="<%= admin_url('/' + m.name) %>"><%= m.display_name %></a></li>
|
8
8
|
<% end %>
|
9
9
|
</ul>
|
10
10
|
</div>
|
@@ -35,7 +35,7 @@
|
|
35
35
|
<td class="checkbox"><input type="checkbox" name="selection[<%= c.id %>]" value="1" id="selection_<%= c.id %>" /></td>
|
36
36
|
<% @model.get_list_properties.each_with_index do |p,i| %>
|
37
37
|
<% v = c.send(p.name.to_sym) || '-' %>
|
38
|
-
<td><% if i == 0 %><a href="/edit
|
38
|
+
<td><% if i == 0 %><a href="<%= admin_url('/edit/' + @model.name + '/' + c.id.to_s) %>"><%= v %></a><% else %><%= v %><% end %></td>
|
39
39
|
<% end %>
|
40
40
|
</tr>
|
41
41
|
<% end %>
|
@@ -45,10 +45,10 @@
|
|
45
45
|
</div>
|
46
46
|
</div>
|
47
47
|
<div class="actions">
|
48
|
-
<form action="/delete
|
48
|
+
<form action="<%= admin_url('/delete/' + @model.name) %>" method="post" id="delete-form">
|
49
49
|
<input type="hidden" id="delete-list" name="id" value="" />
|
50
50
|
<button type="submit" id="delete-button" disabled="disabled" onclick="return confirm('Are you sure you want to delete these items?');">Delete</button>
|
51
51
|
</form>
|
52
|
-
<button onclick="window.location='/new
|
52
|
+
<button onclick="window.location='<%= admin_url('/new/' + @model.name) %>'; return false;">New...</button>
|
53
53
|
</div>
|
54
|
-
<script type="text/javascript" src="/js/index.js"></script>
|
54
|
+
<script type="text/javascript" src="<%= admin_url('/js/index.js') %>"></script>
|
@@ -3,8 +3,13 @@
|
|
3
3
|
<head>
|
4
4
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
5
5
|
<title><%= config[:site_title] %></title>
|
6
|
-
<link rel="stylesheet" href="/css/screen.css" type="text/css" media="screen" />
|
7
|
-
<script type="text/javascript" src="/js/togo.js"></script>
|
6
|
+
<link rel="stylesheet" href="<%= admin_url('/css/screen.css') %>" type="text/css" media="screen" />
|
7
|
+
<script type="text/javascript" src="<%= admin_url('/js/togo.js') %>"></script>
|
8
|
+
<script type="text/javascript">
|
9
|
+
Togo.configure({
|
10
|
+
pathPrefix: '<%= @path_prefix %>'
|
11
|
+
});
|
12
|
+
</script>
|
8
13
|
</head>
|
9
14
|
<body id="<%= @body_id %>">
|
10
15
|
<div id="container">
|
@@ -14,7 +19,7 @@
|
|
14
19
|
<div id="system-menu">
|
15
20
|
<ul>
|
16
21
|
<% config[:system_menu].each do |s| %>
|
17
|
-
<li><a href="<%= s[:url] %>"><%= s[:title] %></a></li>
|
22
|
+
<li><a href="<%= admin_url(s[:url]) %>"><%= s[:title] %></a></li>
|
18
23
|
<% end %>
|
19
24
|
</ul>
|
20
25
|
</div>
|
@@ -3,9 +3,9 @@
|
|
3
3
|
<head>
|
4
4
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
|
5
5
|
<title><%= config[:site_title] %> - Login</title>
|
6
|
-
<link rel="stylesheet" href="/css/login.css" type="text/css" media="screen" />
|
7
|
-
<script type="text/javascript" src="/js/togo.js"></script>
|
8
|
-
<script type="text/javascript" src="/js/login.js"></script>
|
6
|
+
<link rel="stylesheet" href="<%= admin_url('/css/login.css') %>" type="text/css" media="screen" />
|
7
|
+
<script type="text/javascript" src="<%= admin_url('/js/togo.js') %>"></script>
|
8
|
+
<script type="text/javascript" src="<%= admin_url('/js/login.js') %>"></script>
|
9
9
|
</head>
|
10
10
|
<body>
|
11
11
|
<div id="container">
|
@@ -13,7 +13,7 @@
|
|
13
13
|
<% if flash[:error] %>
|
14
14
|
<h3 class="error"><%= flash[:error] %></h3>
|
15
15
|
<% end %>
|
16
|
-
<form action="/login" method="post">
|
16
|
+
<form action="<%= admin_url('/login') %>" method="post">
|
17
17
|
<fieldset>
|
18
18
|
<input type="text" name="username" value="Username" id="username" class="placeholder" />
|
19
19
|
</fieldset>
|
@@ -28,6 +28,6 @@
|
|
28
28
|
login.init();
|
29
29
|
</script>
|
30
30
|
</div>
|
31
|
-
<img src="/img/shadow.png" alt="" style="display: block; margin: 0 auto;" />
|
31
|
+
<img src="<%= admin_url('/img/shadow.png') %>" alt="" style="display: block; margin: 0 auto;" />
|
32
32
|
</body>
|
33
33
|
</html>
|
@@ -1,7 +1,7 @@
|
|
1
|
-
<form action="/create
|
2
|
-
<input type="hidden" name="return_url" value="<%=
|
1
|
+
<form action="<%= admin_url('/create/' + @model.name) %>" method="post" id="edit-form">
|
2
|
+
<input type="hidden" name="return_url" value="<%= return_url %>" />
|
3
3
|
<div id="subhead">
|
4
|
-
<h1><a href="
|
4
|
+
<h1><a href="<%= admin_url('/' + @model.name) %>"><%= @model.display_name %></a> / New</h1>
|
5
5
|
</div>
|
6
6
|
<div id="main">
|
7
7
|
<% if @errors %><div id="save_errors" class="errors"><%= @errors %></div><% end %>
|
@@ -15,5 +15,5 @@
|
|
15
15
|
<button type="submit" class="create">Create</button>
|
16
16
|
</div>
|
17
17
|
</form>
|
18
|
-
<script type="text/javascript" src="/js/edit.js"></script>
|
18
|
+
<script type="text/javascript" src="<%= admin_url('/js/edit.js') %>"></script>
|
19
19
|
|
@@ -28,6 +28,8 @@ module Togo
|
|
28
28
|
|
29
29
|
def initialize(opts = {})
|
30
30
|
@view_path = opts[:view_path] || 'views'
|
31
|
+
@path_prefix = opts[:path_prefix]
|
32
|
+
@path_prefix_regexp = Regexp.new("^#{@path_prefix}")
|
31
33
|
ENV['RACK_ENV'] = (opts[:environment] || :development) if not ENV['RACK_ENV']
|
32
34
|
end
|
33
35
|
|
@@ -50,6 +52,8 @@ module Togo
|
|
50
52
|
def answer(type,path)
|
51
53
|
method = nil
|
52
54
|
@params = symbolize_keys(@request.GET.dup.merge!(@request.POST.dup))
|
55
|
+
path = path.gsub(@path_prefix_regexp,'')
|
56
|
+
path = '/' if path == ''
|
53
57
|
self.class.routes[type].each do |p,k,m|
|
54
58
|
if match = p.match(path)
|
55
59
|
method = m
|
@@ -85,7 +89,7 @@ module Togo
|
|
85
89
|
|
86
90
|
def redirect(location, opts = {})
|
87
91
|
@response.status = (opts[:status] || 301)
|
88
|
-
@response.headers['Location'] = location
|
92
|
+
@response.headers['Location'] = [@path_prefix, location].join
|
89
93
|
@response.finish
|
90
94
|
end
|
91
95
|
|
@@ -123,6 +127,7 @@ module Togo
|
|
123
127
|
subclass.send(:include, Rack::Utils)
|
124
128
|
%w{GET POST}.each{|v| subclass.routes[v] = []}
|
125
129
|
subclass.config = {
|
130
|
+
:path_prefix => '',
|
126
131
|
:public_path => 'public',
|
127
132
|
:static_urls => ['/css','/js','/img'],
|
128
133
|
:port => 8080,
|
@@ -177,7 +182,7 @@ module Togo
|
|
177
182
|
end
|
178
183
|
end
|
179
184
|
end
|
180
|
-
builder.use
|
185
|
+
builder.use Togo::Static, :urls => opts[:static_urls], :root => opts[:public_path], :path_prefix => opts[:path_prefix]
|
181
186
|
if opts[:sessions] or opts[:auth_model]
|
182
187
|
builder.use Rack::Session::Cookie
|
183
188
|
opts[:sessions] = true
|
@@ -194,4 +199,34 @@ module Togo
|
|
194
199
|
|
195
200
|
end # Dispatch
|
196
201
|
|
202
|
+
class Static
|
203
|
+
|
204
|
+
def initialize(app, options={})
|
205
|
+
@app = app
|
206
|
+
@path_prefix = options[:path_prefix] || ''
|
207
|
+
@urls = options[:urls] || ["/favicon.ico"]
|
208
|
+
root = options[:root] || Dir.pwd
|
209
|
+
@file_server = Rack::File.new(root)
|
210
|
+
end
|
211
|
+
|
212
|
+
def call(env)
|
213
|
+
path = env["PATH_INFO"]
|
214
|
+
path = path.gsub(Regexp.new("^#{@path_prefix}"), '') if @path_prefix > ''
|
215
|
+
|
216
|
+
unless @urls.kind_of? Hash
|
217
|
+
can_serve = @urls.any? { |url| path.index(url) == 0 }
|
218
|
+
else
|
219
|
+
can_serve = @urls.key? path
|
220
|
+
end
|
221
|
+
|
222
|
+
if can_serve
|
223
|
+
env["PATH_INFO"] = (@urls.kind_of?(Hash) ? @urls[path] : path)
|
224
|
+
@file_server.call(env)
|
225
|
+
else
|
226
|
+
@app.call(env)
|
227
|
+
end
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
197
232
|
end # Togo
|
@@ -58,6 +58,7 @@
|
|
58
58
|
model: '<%= related_model.name %>',
|
59
59
|
fields: <%= related_model.get_list_properties.map(&:name).to_json %>,
|
60
60
|
count: <%= content_count %>,
|
61
|
-
related_ids: <%= related_ids.to_json
|
61
|
+
related_ids: <%= related_ids.to_json %>,
|
62
|
+
pathPrefix: Togo.config.pathPrefix
|
62
63
|
});
|
63
64
|
</script>
|
@@ -60,6 +60,7 @@
|
|
60
60
|
model: '<%= related_model.name %>',
|
61
61
|
fields: <%= related_model.get_list_properties.map(&:name).to_json %>,
|
62
62
|
count: <%= content_count %>,
|
63
|
-
related_ids: <%= related_ids.to_json
|
63
|
+
related_ids: <%= related_ids.to_json %>,
|
64
|
+
pathPrefix: Togo.config.pathPrefix
|
64
65
|
});
|
65
66
|
</script>
|
@@ -60,7 +60,8 @@
|
|
60
60
|
model: '<%= related_model.name %>',
|
61
61
|
fields: <%= related_model.get_list_properties.map(&:name).to_json %>,
|
62
62
|
count: <%= content_count %>,
|
63
|
-
related_ids: <%= related_ids.to_json
|
63
|
+
related_ids: <%= related_ids.to_json %>,
|
64
|
+
pathPrefix: Togo.config.pathPrefix
|
64
65
|
});
|
65
66
|
</script>
|
66
67
|
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: togo
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 1
|
5
5
|
prerelease: false
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 6
|
9
|
-
-
|
10
|
-
version: 0.6.
|
9
|
+
- 3
|
10
|
+
version: 0.6.3
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Matt King
|
@@ -15,7 +15,7 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2010-11-
|
18
|
+
date: 2010-11-21 00:00:00 -08:00
|
19
19
|
default_executable:
|
20
20
|
dependencies:
|
21
21
|
- !ruby/object:Gem::Dependency
|