togo 0.4.1 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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