togo 0.4.1 → 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,210 @@
1
+ # Introducing Togo
2
+
3
+ Togo is a CMS framework. It also includes a simple web framework based on Rack that can be used to make micro-webapps (similar to Sinatra).
4
+ However the goal of Togo is to be a modular system that can be used with any Ruby ORM framework, giving you a complete CMS system for free.
5
+
6
+ ## Installation
7
+
8
+ gem install togo
9
+
10
+ ## Tutorial
11
+
12
+ Togo works by a simply including a line in your model definition (currently, Togo only works for DataMapper). By including Togo in your model,
13
+ it will be automatically integrated into the Togo admin application, giving you all the CRUD actions as well as listing and search. It even
14
+ works with associations.
15
+
16
+ ### Setting up a simple application
17
+
18
+ We'll use a combination of Sinatra, DataMapper and Sqlite for our tutorial. You actually don't need Sinatra to run the Togo admin application, however this
19
+ gives a good idea of how Togo can be used in conjuction with an existing project.
20
+
21
+ You'll need the following gems installed:
22
+
23
+ pre. dm-core dm-serializer sinatra dm-sqlite3-adapter
24
+
25
+ Next, make a directory for your project like so:
26
+
27
+
28
+ togo-example/
29
+ |
30
+ -- models/
31
+ |
32
+ -- views/
33
+ |
34
+ -- init.rb
35
+ |
36
+ -- app.rb
37
+ |
38
+ -- example.db
39
+
40
+
41
+ ### Set up your models
42
+
43
+ Inside the models directory, create a blog_entry.rb and comment.rb file, and drop in the following:
44
+
45
+
46
+ #blog_entry.rb:
47
+
48
+ class BlogEntry
49
+
50
+ include DataMapper::Resource
51
+ include Togo::DataMapper::Model
52
+
53
+ property :id, Serial
54
+ property :title, String
55
+ property :body, Text
56
+ property :date, DateTime
57
+
58
+ has n, :comments
59
+
60
+ end
61
+
62
+ #comment.rb:
63
+
64
+ class Comment
65
+ include DataMapper::Resource
66
+ include Togo::DataMapper::Model
67
+
68
+ property :id, Serial
69
+ property :name, String
70
+ property :email, String
71
+ property :body, Text
72
+ property :date, DateTime
73
+ property :blog_entry_id, Integer
74
+
75
+ belongs_to :blog_entry
76
+ end
77
+
78
+
79
+ ### Create an init file
80
+
81
+ Togo doesn't set up your database connection or initialize anything needed by your models for you, so you'll have to tell
82
+ it what to do. There are a couple ways of doing it: put the code in a file of your choice and tell Togo to include
83
+ it at runtime, or place the code in a file called togo-admin-config.rb in the same directory that you will run Togo from.
84
+
85
+ In this example, we'll put our initialization in the init.rb file and tell Togo to include it at boot time.
86
+
87
+
88
+ #init.rb:
89
+
90
+ SITE_ROOT = File.dirname(File.expand_path(__FILE__))
91
+ %w(dm-core dm-migrations togo).each{|l| require l}
92
+ Dir.glob(File.join(SITE_ROOT,'models','*.rb')).each{|f| require f}
93
+ DataMapper::Logger.new(STDOUT, :debug)
94
+ DataMapper.setup(:default, "sqlite3://#{Dir.pwd}/example.db")
95
+ DataMapper.auto_upgrade!
96
+
97
+
98
+ ### You're ready to start using Togo
99
+
100
+ At this point you can fire up the Togo admin application. Remember we have to include our init.rb file:
101
+
102
+ togo-admin -r init.rb
103
+
104
+ Open up http://0.0.0.0:8080 in your browser and have a blast! Have a look at our CMS User's Guide for a walkthrough of base CMS functionality (Coming Soon).
105
+
106
+ ## Customizing Togo
107
+
108
+ Now you have a complete CMS system for free, but you can also customize what fields are used, what order they appear, and even
109
+ use your own templates for complete customization.
110
+
111
+ ### Telling Togo which fields to use
112
+
113
+ By default Togo will look at your model and include all available fields in the admin, but you can also tell it specifically which fields
114
+ to use for both the list view and form view.
115
+
116
+ #### Changing the list view
117
+
118
+ If you only want the title and date field to appear in the list view of blog entries, just add in to your model definition:
119
+
120
+
121
+ class BlogEntry
122
+
123
+ include DataMapper::Resource
124
+ include Togo::DataMapper::Model
125
+ ... snipped ...
126
+
127
+ list_properties :title, :date
128
+
129
+ end
130
+
131
+
132
+ Togo will display the fields in the order given. So if you wanted date first some reason, you could just do
133
+
134
+ pre. list_properties :date, :title
135
+
136
+ And date will be listed in the first column.
137
+
138
+ #### Changing the form view (New and Edit views)
139
+
140
+ Just as you can change the list view fields, you can also tell Togo which fields to show in form view and in which order.
141
+
142
+
143
+ class BlogEntry
144
+
145
+ include DataMapper::Resource
146
+ include Togo::DataMapper::Model
147
+ ... snipped ...
148
+
149
+ list_properties :title, :date
150
+ form_properties :title, :date, :body
151
+
152
+ end
153
+
154
+
155
+ Associations also can be used in list and form property declarations:
156
+
157
+
158
+ class BlogEntry
159
+
160
+ include DataMapper::Resource
161
+ include Togo::DataMapper::Model
162
+ ... snipped ...
163
+
164
+ list_properties :title, :date
165
+ form_properties :title, :date, :body, :comments
166
+
167
+ end
168
+
169
+
170
+ #### Configuring properties
171
+
172
+ There are some options you can change on each field individually by using the configure_property declaration.
173
+
174
+ Here you can customize the label:
175
+
176
+ class BlogEntry
177
+
178
+ include DataMapper::Resource
179
+ include Togo::DataMapper::Model
180
+ ... snipped ...
181
+
182
+ list_properties :title, :date
183
+ form_properties :title, :date, :body
184
+
185
+ configure_property :title, :label => "Title (keep it short and sweet)"
186
+
187
+ end
188
+
189
+
190
+ Each property type has it's own default form template that can be overridden simply by specifying a full path to the template (only ERB is currently supported):
191
+
192
+
193
+ class BlogEntry
194
+
195
+ include DataMapper::Resource
196
+ include Togo::DataMapper::Model
197
+ ... snipped ...
198
+
199
+ list_properties :title, :date
200
+ form_properties :title, :date, :body
201
+
202
+ configure_property :title, :label => "Title (keep it short and sweet)"
203
+ configure_property :body, :template => File.join(SITE_ROOT, 'views', 'custom_body.erb')
204
+
205
+ end
206
+
207
+
208
+ 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
+
210
+ See the Writing a Form Template Guide for more information. (Coming Soon)
@@ -1,24 +1,4 @@
1
- module Helpers
2
-
3
- def paging_links(page, count, qs = {})
4
- prev_link, next_link = 'Previous', 'Next'
5
- if not qs.blank?
6
- qs = qs.keys.collect{|k|
7
- [k,escape(qs[k])].join('=') if not qs[k].blank?
8
- }.compact.join('&')
9
- qs = nil if qs.blank?
10
- end
11
-
12
- if not page == 1
13
- prev_link = "<a href=\"?p=#{[page-1, qs].compact.join('&')}\" rel=\"previous\">#{prev_link}</a>"
14
- end
15
- if not page == count and count > 1
16
- next_link = "<a href=\"?p=#{[page+1, qs].compact.join('&')}\" rel=\"next\">#{next_link}</a>"
17
- end
18
- [prev_link, next_link]
19
- end
20
-
21
- end
1
+ require 'helpers'
22
2
 
23
3
  module Togo
24
4
  class Admin < Dispatch
@@ -38,9 +18,10 @@ module Togo
38
18
  @p = (params[:p] || 1).to_i
39
19
  @limit = 50
40
20
  @offset = @limit*(@p-1)
21
+ @order = (params[:o] || "id.desc").split('.').map(&:to_sym)
41
22
  @count = (@q.blank? ? @model.all : @model.search(:q => @q)).size
42
23
  @page_count = @count == 0 ? 1 : (@count.to_f/@limit.to_f).ceil
43
- @criteria = {:limit => @limit, :offset => @offset}
24
+ @criteria = {:limit => @limit, :offset => @offset, :order => @order[0].send(@order[1])}
44
25
  @content = @q.blank? ? @model.all(@criteria) : @model.search(@criteria.merge(:q => @q))
45
26
  erb :index
46
27
  end
@@ -54,7 +35,7 @@ module Togo
54
35
  @content = @model.stage_content(@model.new,params)
55
36
  begin
56
37
  raise "Could not save content" if not @content.save
57
- redirect "/#{@model.name}"
38
+ redirect params[:return_url] || "/#{@model.name}"
58
39
  rescue => detail
59
40
  @errors = detail.to_s
60
41
  erb :new
@@ -70,7 +51,7 @@ module Togo
70
51
  @content = @model.stage_content(@model.get(params[:id]),params)
71
52
  begin
72
53
  raise "Could not save content" if not @content.save
73
- redirect "/#{@model.name}"
54
+ redirect params[:return_url] || "/#{@model.name}"
74
55
  rescue => detail
75
56
  @errors = detail.to_s
76
57
  erb :edit
@@ -83,7 +64,7 @@ module Togo
83
64
  @items.each do |i|
84
65
  @model.delete_content(i)
85
66
  end
86
- redirect "/#{@model.name}"
67
+ redirect params[:return_url] || "/#{@model.name}"
87
68
  rescue => detail
88
69
  @errors = detail.to_s
89
70
  @content = params[:q] ? @model.search(:q => params[:q]) : @model.all
@@ -0,0 +1,57 @@
1
+ module Helpers
2
+
3
+ class FlashHash
4
+ def initialize
5
+ @h = {}
6
+ @c = {}
7
+ end
8
+ def [](key)
9
+ return @c[key] if @c.keys.include?(key)
10
+ @c[key] = @h.delete(key) if @h.keys.include?(key)
11
+ end
12
+ def []=(key,val)
13
+ @h[key] = val
14
+ end
15
+ def sweep!
16
+ @c = {}
17
+ end
18
+ end
19
+
20
+ def hash_to_qs(h)
21
+ return nil if h.blank?
22
+ qs = h.keys.collect{|k|
23
+ [k,escape(h[k])].join('=') if not h[k].blank?
24
+ }.compact.join('&')
25
+ qs = nil if qs.blank?
26
+ qs
27
+ end
28
+
29
+ def paging_links(page, count, qs = {})
30
+ prev_link, next_link = 'Previous', 'Next'
31
+ qs = hash_to_qs(qs)
32
+
33
+ if not page == 1
34
+ prev_link = "<a href=\"?p=#{[page-1, qs].compact.join('&')}\" rel=\"previous\">#{prev_link}</a>"
35
+ end
36
+ if not page == count and count > 1
37
+ next_link = "<a href=\"?p=#{[page+1, qs].compact.join('&')}\" rel=\"next\">#{next_link}</a>"
38
+ end
39
+ [prev_link, next_link]
40
+ end
41
+
42
+ def column_head_link(property, current_order, qs = {})
43
+ qs = hash_to_qs(qs)
44
+ new_order = (current_order[0] == property.name.to_sym ? (current_order[1] == :asc ? "desc" : "asc") : "asc")
45
+ link_class = current_order[0] == property.name.to_sym ? new_order : ''
46
+ "<a href=\"?o=#{[(property.name.to_s+'.'+new_order.to_s),qs].compact.join('&')}\" class=\"#{link_class}\">#{property.name.to_s.humanize.titleize}</a>"
47
+ end
48
+
49
+ def partial(template, options={})
50
+ erb template, options.merge(:layout => false)
51
+ end
52
+
53
+ def active_menu(name)
54
+ request.path =~ /#{name}/ ? 'active' : ''
55
+ end
56
+
57
+ end