fullstack-admin 0.2.1 → 0.2.2

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/TODO.tasks CHANGED
@@ -2,7 +2,8 @@
2
2
  = Fullstack Admin Roadmap =
3
3
  ===========================
4
4
 
5
- - Fix positionable/_collection.html.erb not working
5
+ - Fix assets compilation issues (and lock assets-related gems to working versions only)
6
+ ✓ Fix positionable/_collection.html.erb not working
6
7
 
7
8
  - Has many nested:
8
9
  has_many_nested = has_many + accepts_nested_attributes_for + :allow_destroy => true [CORE]
@@ -33,17 +34,17 @@
33
34
 
34
35
  ✓ Ckeditor: upload assets to S3 according to app.config
35
36
 
36
- - Localizable models
37
- - Create Localized module: a model is localizable if has a :locale field (cms?)
38
- - Add a scope to Localized to find models within the current locale (cms?)
37
+ Localizable models
38
+ Create Localized module: a model is localizable if has a :locale field (cms?)
39
+ Add a scope to Localized to find models within the current locale (cms?)
39
40
 
40
- - Create an option to specify the admin_locale
41
- - add a before filter to Admin::BaseController that uses the default locale for the admin
42
- - Split localized models index into tabs (either through ajax?)
43
- - Let the programmer decide a default locale and a set of available locales
44
- - Translations for locale codes
41
+ Create an option to specify the admin_locale
42
+ add a before filter to Admin::BaseController that uses the default locale for the admin
43
+ Split localized models index into tabs (either through ajax?)
44
+ Let the programmer decide a default locale and a set of available locales
45
+ Translations for locale codes
45
46
 
46
- - Form for accepts_nested_attributes_for and has_one
47
+ - Form for accepts_nested_attributes_for and has_one (NOT TESTED?)
47
48
  - Optional tracking of author/updaters for every model (CMS?)
48
49
 
49
50
  ==================
@@ -55,9 +56,9 @@
55
56
  - find alternative to sortable
56
57
 
57
58
  - Multiple scopes
58
- - Tags input with chosen
59
+ - Tags input with select2
59
60
 
60
- - Alternated fields:
61
+ - Conditional fields and Virtual Fields:
61
62
  = Group of fields that can be setted exclusively
62
63
  = eg
63
64
  = field :age
@@ -65,5 +66,10 @@
65
66
 
66
67
  = virtual_field :link_method, :in => %W(page external)
67
68
 
68
- = belongs_to :related_page, :meaningful_if => :link_method.eq("page")
69
- = field :url, :meaningful_if => :link_method.eq("external")
69
+ = belongs_to :related_page, :meaningful_when => :link_method.eq("page")
70
+ = field :url, :meaningful_when => :link_method.eq("external")
71
+
72
+ - Validation DSL
73
+ = field :name, :string, :required
74
+ = field :email, :required, :unique
75
+
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.2.1
1
+ 0.2.2
@@ -1,4 +1,5 @@
1
1
  /*
2
+ *= require admin/login
2
3
  *= require support/base
3
4
  *= require support/bootstrap
4
5
  *= require support/forms
@@ -13,15 +14,113 @@
13
14
  }
14
15
  }
15
16
 
17
+ input {
18
+ -moz-box-sizing: border-box;
19
+ -webkit-box-sizing: border-box;
20
+ box-sizing: border-box;
21
+ width: 100%;
22
+ height: auto !important;
23
+ }
24
+
25
+ /* ========= */
26
+ /* = Icons = */
27
+ /* ========= */
28
+
29
+ .icon-white, .nav-tabs > .active > a > [class^="icon-"], .nav-tabs > .active > a > [class*=" icon-"], .nav-pills > .active > a > [class^="icon-"], .nav-pills > .active > a > [class*=" icon-"], .nav-list > .active > a > [class^="icon-"], .nav-list > .active > a > [class*=" icon-"], .navbar-inverse .nav > .active > a > [class^="icon-"], .navbar-inverse .nav > .active > a > [class*=" icon-"], .dropdown-menu > li > a:hover > [class^="icon-"], .dropdown-menu > li > a:hover > [class*=" icon-"], .dropdown-menu > .active > a > [class^="icon-"], .dropdown-menu > .active > a > [class*=" icon-"] {
30
+ background-image: url("/img/glyphicons-halflings-white.png");
31
+ }
32
+
33
+ /* ========== */
34
+ /* = Navbar = */
35
+ /* ========== */
36
+
37
+ .navbar .nav li.dropdown > .dropdown-toggle .caret {
38
+ border-top-color: white;
39
+ border-bottom-color: white;
40
+ }
41
+
42
+ .navbar-inner {
43
+ background: #3993ba;
44
+ background: -moz-linear-gradient(top, #3993ba 0%, #067ead 100%);
45
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#3993ba), color-stop(100%,#067ead));
46
+ background: -webkit-linear-gradient(top, #3993ba 0%,#067ead 100%);
47
+ background: -o-linear-gradient(top, #3993ba 0%,#067ead 100%);
48
+ background: -ms-linear-gradient(top, #3993ba 0%,#067ead 100%);
49
+ background: linear-gradient(top, #3993ba 0%,#067ead 100%);
50
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#3993ba', endColorstr='#067ead',GradientType=0 );
51
+ -webkit-box-shadow: none;
52
+ -moz-box-shadow: none;
53
+ box-shadow: none;
54
+
55
+ }
56
+
57
+ .navbar .nav > li > a {
58
+ color: #c1dce7;
59
+ }
60
+
61
+ .navbar .nav > li:hover > a {
62
+ color:#fff;
63
+ }
64
+
65
+ .navbar .nav .active > a, .navbar .nav .active > a:hover, .navbar .nav li.dropdown.open > .dropdown-toggle {
66
+ background: #206484;
67
+ color: #fff;
68
+ }
69
+
70
+ .navbar .divider-vertical {
71
+ background-color:#2078A1;
72
+ border-color:#3497C2;
73
+ }
74
+
75
+ .navbar .divider-vertical {
76
+ border-left-color: #2078A1;
77
+ border-right-color: #3497C2;
78
+ }
79
+
80
+ .dropdown-menu li > a:hover, .dropdown-menu .active > a,
81
+ .dropdown-menu .active > a:hover,
82
+ .nav-list > .active > a, .nav-list > .active > a:hover {
83
+ background: #48a6d2 !important;
84
+ }
85
+
86
+ .table thead th {background-color:#ebf2f6 !important}
87
+ .dataTables_wrapper th.sorting_asc,.dataTables_wrapper th.sorting_desc {background-color:#d4e3eb !important}
88
+
89
+
16
90
  #left-aside .nav {
17
91
  padding-bottom: 180px;
18
92
  }
19
93
 
94
+ .navbar .nav > li > a {
95
+ padding-top: 10px;
96
+ }
97
+ .navbar .nav > li > a {
98
+ text-shadow: none;
99
+ padding: 9px 10px 11px;
100
+ }
101
+ .navbar .nav > li > a {
102
+ color: #C1DCE7;
103
+ }
104
+
105
+ .navbar .brand {
106
+ width: 200px;
107
+ font: 100 16px/16px 'PT Sans', sans-serif;
108
+ text-decoration: none;
109
+ color: #c1dce7;
110
+ text-shadow: none;
111
+ padding: 10px 20px 0;
112
+ }
113
+
114
+ .navbar .brand:hover {
115
+ color: #fff;
116
+ }
117
+
20
118
  .sidenav > li:first-child {
21
119
  -webkit-border-radius: 6px 6px 0 0;
22
120
  -moz-border-radius: 6px 6px 0 0;
23
121
  border-radius: 6px 6px 0 0;
24
122
  }
123
+
25
124
  .sidenav > li {
26
125
  display: block;
27
126
  margin: 0 0 -1px;
@@ -31,7 +130,6 @@ margin-left: -15px;
31
130
  margin-right: -15px;
32
131
  }
33
132
 
34
-
35
133
  .thumbnails > li {
36
134
  float: left;
37
135
  margin-left: 0 !important;
@@ -58,4 +156,217 @@ margin-right: -15px;
58
156
  background: url(/assets/iconic/gray/magnifying_glass_12x12.png) no-repeat left center;
59
157
  margin-left: 7px !important;
60
158
  padding-left: 12px !important;
159
+ }
160
+
161
+ /* ======= */
162
+ /* = Box = */
163
+ /* ======= */
164
+
165
+ .box {
166
+ -webkit-box-shadow: 0px 1px 2px 0px #EFEFEF;
167
+ -moz-box-shadow: 0px 1px 2px 0px #EFEFEF;
168
+ box-shadow: 0px 1px 2px 0px #EFEFEF;
169
+ margin-bottom: 20px;
170
+ }
171
+
172
+ .box-header, .box-content, .box-footer {
173
+ padding: 20px;
174
+ }
175
+
176
+ .box-header {
177
+ height: 32px;
178
+ line-height: 32px;
179
+ border: 1px solid #DDD;
180
+ padding: 0 10px;
181
+ background: #FBFBFB;
182
+ background: -moz-linear-gradient(top, #FBFBFB 0%, #F1F1F1 100%);
183
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#FBFBFB), color-stop(100%,#F1F1F1));
184
+ background: -webkit-linear-gradient(top, #FBFBFB 0%,#F1F1F1 100%);
185
+ background: -o-linear-gradient(top, #FBFBFB 0%,#F1F1F1 100%);
186
+ background: -ms-linear-gradient(top, #FBFBFB 0%,#F1F1F1 100%);
187
+ background: linear-gradient(top, #FBFBFB 0%,#F1F1F1 100%);
188
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#fbfbfb', endColorstr='#f1f1f1',GradientType=0 );
189
+ font-weight: 700;
190
+ color: #666;
191
+ font-size: 11px;
192
+ }
193
+
194
+
195
+ .box-content {
196
+ padding: 10px;
197
+ border: 1px solid #DDD;
198
+ border-top: none;
199
+ }
200
+
201
+ .box-content:last-child {
202
+ margin-bottom: 0;
203
+ padding-bottom: 0;
204
+ }
205
+
206
+ .box-footer {
207
+ border: 1px solid #DDD;
208
+ padding: 8px 10px;
209
+ background: #F1F1F1;
210
+ border-top: none;
211
+ }
212
+
213
+ .box-footer.align-right {
214
+ text-align: right;
215
+ }
216
+
217
+ fieldset.box-content {
218
+ padding-top: 20px;
219
+ }
220
+
221
+ /* ========== */
222
+ /* = Layout = */
223
+ /* ========== */
224
+
225
+ a, button, input {
226
+ outline: none !important;
227
+ }
228
+
229
+ a {
230
+ color: #08C;
231
+ text-decoration: none;
232
+ }
233
+
234
+ body {
235
+ padding-top: 60px;
236
+ }
237
+
238
+ .main {
239
+ background: white;
240
+ border-left: 1px solid transparent;
241
+ margin-left: 240px;
242
+ }
243
+
244
+
245
+ /* =============== */
246
+ /* = Page Header = */
247
+ /* =============== */
248
+
249
+ .resource_updated_at {
250
+ font: italic 12px/32px Arial, Helvetica, sans-serif;
251
+ color: #888;
252
+ }
253
+
254
+ .page-header h1, h1.page-header{
255
+ line-height: 34px;
256
+ font-size: 18px;
257
+ margin-top: 0;
258
+ }
259
+ .page-header h1 {
260
+ margin-bottom: 0;
261
+ }
262
+
263
+ #center > .page-header {
264
+ margin-top: 0;
265
+ margin-bottom: 20px;
266
+ padding: 0;
267
+ }
268
+
269
+ .index-actions {
270
+ float:right;
271
+ }
272
+
273
+
274
+ /* =========== */
275
+ /* = Sidebar = */
276
+ /* =========== */
277
+
278
+ .sidebar {
279
+ position: fixed;
280
+ top: 40px;
281
+ left: 0;
282
+ margin-left: 0;
283
+ padding-top: 20px;
284
+ width: 240px;
285
+ background: #f1f1f1;
286
+ border-right: 1px solid #ccc;
287
+ height: 100%;
288
+ font: 13px/18px "Helvetica Neue",Helvetica,Arial,sans-serif;
289
+ }
290
+
291
+ .sidebar .accordion {
292
+ border-top: 1px solid #CCC;
293
+ margin-bottom: 20px;
294
+ }
295
+
296
+ .sidebar .accordion-heading a:hover {
297
+ background-color: #CFCFCF;
298
+ }
299
+
300
+ .sidebar .accordion-heading {
301
+ text-shadow: 1px 1px 0 #EFEFEF;
302
+ background: #E0E0E0;
303
+ -webkit-box-shadow: inset 0px 1px 0px 0px #ECECEC;
304
+ box-shadow: inset 0px 1px 0px 0px #ECECEC;
305
+ }
306
+
307
+ .sidebar .accordion-inner {
308
+ border-top: 1px solid #CCC;
309
+ background: #FAFAFA;
310
+ }
311
+
312
+ .sidebar .accordion-group .accordion-heading a {
313
+ color: #222;
314
+ }
315
+
316
+ .sidebar .accordion-group .active a {
317
+ color: white;
318
+ }
319
+
320
+ .nav-list > li > a, .dropdown-menu li a {
321
+ -webkit-border-radius: 4px;
322
+ -moz-border-radius: 4px;
323
+ -ms-border-radius: 4px;
324
+ border-radius: 4px;
325
+ }
326
+
327
+ .dropdown-menu li > a:hover, .dropdown-menu .active > a, .dropdown-menu .active > a:hover, .nav-list > .active > a, .nav-list > .active > a:hover {
328
+ background: #48A6D2 !important;
329
+ }
330
+
331
+ .nav > li > a:hover {
332
+ text-decoration: none;
333
+ background-color: #EEE;
334
+ }
335
+
336
+ .sidebar .accordion-group a {
337
+ color: #222;
338
+ text-decoration: none!important;
339
+ }
340
+
341
+ .sidebar .accordion-group {
342
+ -webkit-border-radius: 0;
343
+ -moz-border-radius: 0;
344
+ border-radius: 0;
345
+ margin-bottom: 0;
346
+ border-color: #CCC;
347
+ border-style: solid;
348
+ border-width: 0 0 1px;
349
+ }
350
+
351
+ .accordion-heading .accordion-toggle {
352
+ display: block;
353
+ padding: 7px 15px;
354
+ }
355
+
356
+ .accordion-toggle {
357
+ -webkit-transition: background-color 0.2s ease-in-out;
358
+ -moz-transition: background-color 0.2s ease-in-out;
359
+ -o-transition: background-color 0.2s ease-in-out;
360
+ transition: background-color 0.2s ease-in-out;
361
+ }
362
+ .accordion-toggle {
363
+ cursor: pointer;
364
+ }
365
+
366
+ /* ================ */
367
+ /* = Filter Input = */
368
+ /* ================ */
369
+
370
+ .filter-inputs {
371
+ margin: 0;
61
372
  }
@@ -0,0 +1,54 @@
1
+ .login-box {
2
+ width: 380px;
3
+ margin: auto;
4
+ padding: 0;
5
+ position: relative;
6
+ top: 50%;
7
+ background: white;
8
+ border: 1px solid #CCC;
9
+ -webkit-border-radius: 6px;
10
+ -moz-border-radius: 6px;
11
+ -ms-border-radius: 6px;
12
+ border-radius: 6px;
13
+ -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
14
+ -moz-box-shadow: 0 0 6px rgba(0,0,0,0.2);
15
+ -ms-box-shadow: 0 0 6px rgba(0,0,0,0.2);
16
+ box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
17
+ }
18
+
19
+ .login-box-header, .login-box-content, .form-actions.login-box-footer {
20
+ padding: 20px;
21
+ margin: 0;
22
+ }
23
+
24
+ .login-box-header {
25
+ text-shadow: 0 1px 0 rgba(255, 255, 255, .5);
26
+ font: 100 18px/42px 'PT Sans', sans-serif;
27
+ height: 42px;
28
+ padding: 0 20px;
29
+ background: #E0E0E0;
30
+ border-bottom: 1px solid #CCC;
31
+ -moz-border-radius-topleft: 6px;
32
+ -moz-border-radius-topright: 6px;
33
+ -moz-border-radius-bottomright: 0px;
34
+ -moz-border-radius-bottomleft: 0px;
35
+ -webkit-border-radius: 6px 6px 0px 0px;
36
+ border-radius: 6px 6px 0px 0px;
37
+ font-size: 15px;
38
+ }
39
+
40
+ .login-box input[type="text"], .login-box input[type="password"] {
41
+ width: 298px;
42
+ }
43
+
44
+ .login-box-footer {
45
+ padding: 12px 20px;
46
+ border-top: 1px solid #E7E7E7;
47
+ background: #F7F7F7;
48
+ -moz-border-radius-topleft: 0px;
49
+ -moz-border-radius-topright: 0px;
50
+ -moz-border-radius-bottomright: 6px;
51
+ -moz-border-radius-bottomleft: 6px;
52
+ -webkit-border-radius: 0px 0px 6px 6px;
53
+ border-radius: 0px 0px 6px 6px;
54
+ }
@@ -8,7 +8,7 @@
8
8
  .badge.badge-mini {
9
9
  padding: 0 5px;
10
10
  font-size: 9px;
11
- display: inline-block;}
11
+ display: inline-block;
12
12
  line-height: 18px;
13
13
  }
14
14
 
@@ -1,17 +1,13 @@
1
1
  class Admin::BaseController < ApplicationController
2
- rescue_from Checkin::AccessDenied, :with => :rescue_access_denied
3
-
2
+ before_filter :require_login
3
+ before_filter :fetch_current_resource
4
+
4
5
  layout 'admin'
5
- authorize(:scope => :admin)
6
6
 
7
7
  protected
8
8
 
9
- def rescue_access_denied
10
- if subject.guest?
11
- redirect_to new_admin_session_path
12
- else
13
- render :text => "Not Authorized", :status => 403
14
- end
9
+ def not_authenticated
10
+ redirect_to new_admin_session_url, :alert => "First login to access this page."
15
11
  end
16
12
 
17
13
  class << self
@@ -27,19 +23,24 @@ class Admin::BaseController < ApplicationController
27
23
  :current_resource_class,
28
24
  :current_resource,
29
25
  :current_collection,
30
- :title_column
26
+ :title_column,
27
+ :subject
31
28
 
32
29
 
30
+ def subject
31
+ @subject ||= ::Admin::SubjectModelAdapter.new(current_user)
32
+ end
33
+
33
34
  def current_resource_class
34
- @current_resource_class ||= controller_name.singularize.camelize.constantize
35
+ @current_resource_class ||= controller_name.singularize.camelize.constantize rescue nil
35
36
  end
36
37
 
37
38
  def resource_name
38
- current_resource_class.name.demodulize.underscore
39
+ current_resource_class && current_resource_class.name.demodulize.underscore
39
40
  end
40
41
 
41
42
  def collection_name
42
- resource_name.pluralize
43
+ resource_name.try(:pluralize)
43
44
  end
44
45
 
45
46
  alias :singular_name :resource_name
@@ -59,5 +60,9 @@ class Admin::BaseController < ApplicationController
59
60
  @_title_columns[model] ||= ( model.column_names.map{ |c| c.to_s } & %W(title name label browser_title seo_title seo_name key claim email) ).first
60
61
  end
61
62
 
63
+ def fetch_current_resource
64
+ return if !params[:id] || current_resource
65
+ instance_variable_set("@#{resource_name}", current_resource_class.find(params[:id]))
66
+ end
62
67
 
63
- end
68
+ end
@@ -1,19 +1,27 @@
1
1
  class Admin::SessionsController < ApplicationController
2
- layout 'admin'
2
+ layout 'login'
3
+
4
+ def new
5
+ @user = Superuser.new
6
+ end
3
7
 
4
8
  def create
5
- user = login(params[:email], params[:password], params[:remember_me])
6
- if user
7
- redirect_back_or_to root_url, :notice => "Logged in!"
8
- else
9
- flash.now.alert = "Email or password was invalid"
10
- render :new
9
+ respond_to do |format|
10
+ if @user = login(params[:username],params[:password])
11
+ format.html { redirect_back_or_to("/", :notice => I18n.t("signed_in", :scope => "fullstack.admin", :default => 'Signed in successfully.')) }
12
+ format.xml { render :xml => @user, :status => :created, :location => @user }
13
+ else
14
+ format.html { flash.now[:alert] = I18n.t("login_failed", :scope => "fullstack.admin", :default => 'Login failed.'); render :action => "new" }
15
+ format.xml { render :xml => @user.errors, :status => :unprocessable_entity }
16
+ end
11
17
  end
12
18
  end
13
-
19
+
14
20
  def destroy
15
21
  logout
16
- redirect_to root_url, :notice => "Logged out!"
22
+ redirect_to("/", :notice => I18n.t("signed_out", :scope => "fullstack.admin", :default => 'Signed out successfully.'))
17
23
  end
18
24
 
19
- end
25
+ end
26
+
27
+
@@ -0,0 +1,57 @@
1
+ class Admin::SubjectModelAdapter
2
+ attr_accessor :subject_model
3
+
4
+ def initialize(subject_model)
5
+ @subject_model = subject_model
6
+ end
7
+
8
+ def guest?
9
+ !subject_model
10
+ end
11
+
12
+ def logged_in?
13
+ !!subject_model
14
+ end
15
+
16
+ def owner?(object)
17
+ object && ( object.respond_to?(:author) && ( subject_model == object.author ) ) || ( object.respond_to?(:owner) && ( subject_model == object.owner ) )
18
+ end
19
+ alias :own? :owner?
20
+
21
+ def administrator?
22
+ true
23
+ end
24
+
25
+ def can?(*args)
26
+ true
27
+ end
28
+
29
+ def can_edit?(*args)
30
+ true
31
+ end
32
+
33
+ def can_create?(*args)
34
+ true
35
+ end
36
+
37
+ def can_destroy?(*args)
38
+ true
39
+ end
40
+
41
+ def can_new?(*args)
42
+ true
43
+ end
44
+
45
+ def can_update?(*args)
46
+ true
47
+ end
48
+
49
+ def can_sort?(*args)
50
+ true
51
+ end
52
+
53
+ def can_show?(*args)
54
+ true
55
+ end
56
+
57
+ end
@@ -1,12 +1,6 @@
1
1
  class Superuser < ActiveRecord::Base
2
2
  authenticates_with_sorcery!
3
-
4
- attr_accessible :email, :password, :password_confirmation
5
-
6
- validates_confirmation_of :password
7
- validates_presence_of :password, :on => :create
8
- validates_presence_of :email
9
- validates_uniqueness_of :email
10
- field :email
11
- field :password
12
- end
3
+ def has_role?(r)
4
+ true
5
+ end
6
+ end
@@ -1,13 +1,15 @@
1
- <% content_for :menu do -%>
1
+ <% content_for :menu do -%>
2
+
3
+ <%= nav(:class => 'float right') do %>
4
+ <li class="divider-vertical">
5
+ </li>
2
6
 
3
- <%= nav do %>
4
- <%= nav_item t('fullstack.admin.dashboard', :default => "Dashboard"), admin_root_path, :icon => "cog" %>
5
- <% end %>
7
+ <%= nav_item t('fullstack.admin.homepage', :default => "Homepage"), "/", :icon => "globe", :"icon_color" => :white %>
8
+ <li class="divider-vertical">
9
+ </li>
6
10
 
7
- <%= nav(:class => 'float right') do %>
8
- <%= nav_item t('fullstack.admin.homepage', :default => "Homepage"), "/", :icon => "home" %>
9
- <%= dropdown_nav_item t('fullstack.admin.account', :default => "Account"), :icon => "user" do %>
10
- <%= nav_item t('fullstack.admin.logout', :default => "Logout"), destroy_user_session_path, :method => :delete %>
11
+ <%= dropdown_nav_item t('fullstack.admin.account', :default => "Account"), :icon => "user", :"icon_color" => :white do %>
12
+ <%= nav_item t('fullstack.admin.logout', :default => "Logout"), admin_sessions_path, :method => :delete %>
11
13
  <% end %>
12
14
  <% end %>
13
15
 
@@ -15,19 +17,34 @@
15
17
 
16
18
  <% content_for :nav do -%>
17
19
 
18
- <div class="tabbable tabs-left float right">
19
- <%= nav_list :class => "nav-tabs" do %>
20
20
 
21
- <% Fullstack::Admin.resources.each do |rog| %>
22
- <% if rog.type == :group %>
23
- <%= nav_header t("fullstack.admin.groups.#{rog.name}", :default => rog.name.to_s.humanize) %>
24
-
25
- <% elsif rog.type == :resource %>
26
- <%= nav_item t("fullstack.admin.resources.#{rog.name}", :default => rog.name.to_s.humanize), [:admin, rog.name] %>
27
- <% end %>
28
- <% end %>
29
-
30
- <% end %>
31
- </div>
21
+ <div class="sidebar">
22
+ <div class="sidebar-inner">
23
+ <div class="accordion sidebar-accordion" id="sidebar-accordion">
24
+ <% i = 0 %>
25
+ <% Fullstack::Admin.grouped_resources.each do |group, resources| %>
26
+ <div class="accordion-group">
27
+ <div class="accordion-heading">
28
+ <a href="#sidebar_group_collapsable_<%= i+=1 %>" data-parent="#sidebar-accordion" data-toggle="collapse" class="accordion-toggle">
29
+ <i class="icon-<%= group.icon %>"></i> <%= t("fullstack.admin.groups.#{group.name}", :default => group.name.to_s.humanize) %>
30
+ <% if resources.map(&:name).include?(plural_name) %>
31
+ <i class="icon-chevron-left float right" style="opacity: 0.4;"></i>
32
+ <% end %>
33
+ </a>
34
+ </div>
35
+ <div class="accordion-body collapse <%= resources.map(&:name).include?(plural_name) ? 'in' : '' %>" id="sidebar_group_collapsable_<%= i %>">
36
+ <div class="accordion-inner">
37
+ <ul class="nav nav-list">
38
+ <% resources.each do |resource| %>
39
+ <%= nav_item t("fullstack.admin.resources.#{resource.name}", :default => resource.name.to_s.humanize), [:admin, resource.name] %>
40
+ <% end %>
41
+ </ul>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ <% end %>
46
+ </div>
47
+ </div>
48
+ </div>
32
49
 
33
- <% end -%>
50
+ <% end %>
@@ -1,10 +1,43 @@
1
+ <div class="page-header">
2
+ <% unless partial?('collection')%>
3
+ <div class="mb1 index-actions">
4
+ <% if subject.can_create?(collection_name) && controller.action_methods.include?("new") %>
5
+ <%= button t('fullstack.admin.new', :default => "New"),
6
+ send("new_admin_#{resource_name}_path"),
7
+ :type => :primary, :icon => :plus, :icon_color => :white %>
8
+ <% end %>
9
+
10
+ <% if subject.can_sort?(collection_name) && positionable?(current_collection.klass) %>
11
+ <%= button t('fullstack.admin.sort', :default => "Sort"),
12
+ send("admin_positionables_path", :type => resource_name)
13
+ %>
14
+
15
+ <% end %>
16
+
17
+ <%= button t('fullstack.admin.delete', :default => "Delete"),
18
+ 'javascript:void(0)', :class => "toggle-delete",
19
+ :icon => :trash %>
20
+ </div>
21
+
22
+ <% end %>
23
+
24
+ <h1>
1
25
  <% if content_for?(:title) %>
2
- <div class="page-header"><h1><%= yield(:title) %></h1></div>
26
+ <%= yield(:title) %>
27
+
3
28
  <% elsif @title %>
4
- <div class="page-header"><h1><%= @title %></h1></div>
29
+
30
+ <%= @title %>
31
+
5
32
  <% else -%>
6
- <div class="page-header"><h1><%= t(collection_name, :scope => "fullstack.admin.resources") %></h1></div>
33
+
34
+ <%= t(collection_name, :scope => "fullstack.admin.resources") %>
35
+
7
36
  <% end -%>
37
+ </h1>
38
+
39
+
40
+ </div>
8
41
 
9
42
  <% if partial?('collection') %>
10
43
  <%= render :partial => "collection",
@@ -15,24 +48,7 @@
15
48
  %>
16
49
  <% else %>
17
50
 
18
- <div class="mb1">
19
- <% if subject.can_create?(collection_name) && controller.action_methods.include?("new") %>
20
- <%= button t('fullstack.admin.new', :default => "New"),
21
- send("new_admin_#{resource_name}_path"),
22
- :type => :primary, :icon => :plus, :icon_color => :white %>
23
- <% end %>
24
-
25
- <% if subject.can_sort?(collection_name) && positionable?(current_collection.klass) %>
26
- <%= button t('fullstack.admin.sort', :default => "Sort"),
27
- send("admin_positionables_path", :type => resource_name)
28
- %>
29
-
30
- <% end %>
31
-
32
- <%= button t('fullstack.admin.delete', :default => "Delete"),
33
- 'javascript:void(0)', :class => "toggle-delete",
34
- :icon => :trash %>
35
- </div>
51
+
36
52
 
37
53
 
38
54
  <table class="table table-bordered table-striped index-table">
@@ -56,16 +72,18 @@
56
72
  <% content_for :aside do -%>
57
73
 
58
74
  <% if (!@skip_filter) && (partial?('filter') || title_column(current_resource_class) || has_timestamps?(current_resource_class)) %>
59
- <div class="well">
60
- <div class="mb1">
61
- <h4><%= t('fullstack.admin.filter', :default => "Filter") %></h4>
75
+ <div class="box">
76
+ <div class="box-header">
77
+ <%= t('fullstack.admin.filter', :default => "Filter") %>
62
78
  </div>
79
+
63
80
  <%= admin_form_for @search, :url => self.send("admin_#{collection_name}_path") , :html => {:method => :get} do |f| %>
81
+ <div class="box-content">
64
82
 
65
83
  <% if partial?('filter') %>
66
84
  <%= render :partial => "filter", :locals => {:f => f} %>
67
85
  <% else %>
68
- <%= f.inputs do %>
86
+ <%= f.inputs :class => "filter-inputs" do %>
69
87
 
70
88
  <% if tc = title_column(current_resource_class) %>
71
89
  <%= f.input :"#{tc}_contains" %>
@@ -78,9 +96,10 @@
78
96
  <% end %>
79
97
 
80
98
  <% end %>
81
- <%= f.actions do %>
82
- <%= f.action t('fullstack.admin.filter', :default => "Filter"), :as => :button %>
83
- <% end %>
99
+ </div>
100
+ <div class="box-footer align-right">
101
+ <%= f.action t('fullstack.admin.filter', :default => "Filter"), :as => :button %>
102
+ </div>
84
103
  <% end %>
85
104
  </div>
86
105
  <% end %>
@@ -0,0 +1,34 @@
1
+ <%= form_tag admin_sessions_path, :method => :post, :class => "login-box" do %>
2
+ <div class="login-box-header">
3
+ Login
4
+ </div>
5
+ <div class="login-box-content">
6
+
7
+ <% [:notice, :error, :alert].each do |sym| %>
8
+
9
+ <% if flash[sym].present? %>
10
+ <div class="alert alert-info alert-login ">
11
+ <%= flash[sym] %>
12
+ </div>
13
+ <% flash.discard(sym) %>
14
+ <% end %>
15
+
16
+ <% end %>
17
+
18
+
19
+ <div class="input-prepend">
20
+ <span class="add-on"><i class="icon-user"></i></span>
21
+ <%= text_field_tag :username, nil, :placeholder => I18n.t("username", :scope => "helpers.label", :default => "Username") %>
22
+ </div>
23
+
24
+ <div class="input-prepend">
25
+ <span class="add-on"><i class="icon-lock"></i></span>
26
+ <%= password_field_tag :password, nil, :placeholder => I18n.t("password", :scope => "helpers.label", :default => "Password") %>
27
+ </div>
28
+ </div>
29
+
30
+ <div class="actions form-actions login-box-footer">
31
+ <%= submit_tag "Login", :class => "btn btn-primary" %>
32
+ </div>
33
+
34
+ <% end %>
@@ -12,25 +12,23 @@
12
12
  <%= render :partial => 'admin/nav' %>
13
13
 
14
14
  <%= navbar :fluid => true, :fixed => :top do %>
15
- <%= brand "#{app_name} Admin", admin_root_path %>
15
+ <a class="brand" href="/admin">
16
+ <i class="icon-home icon-white"></i> <%= "#{app_name} Admin" %></a>
16
17
  <%= yield :menu %>
17
18
  <% end %>
18
19
 
19
20
  <%= yield :crumbs %>
21
+ <%= yield :nav %>
20
22
 
21
23
  <%= container :fluid => true, :class => "main" do %>
22
- <%= row do %>
24
+ <%= row do %>
23
25
 
24
- <%= span(2, :id => "left-aside") do %>
25
- <%= yield :nav %>
26
- <% end %>
27
-
28
- <%= span(content_for?(:aside) ? 6 : 10, :id => "center") do %>
26
+ <%= span(content_for?(:aside) ? 9 : 12, :id => "center") do %>
29
27
  <%= yield %>
30
28
  <% end %>
31
29
 
32
30
  <% if content_for?(:aside) %>
33
- <%= span(4, :id => "left-aside") do %>
31
+ <%= span(3, :id => "right-aside") do %>
34
32
  <%= yield :aside %>
35
33
  <% end %>
36
34
  <% end %>
@@ -0,0 +1,15 @@
1
+ <!DOCTYPE html>
2
+ <html lang="it">
3
+ <head>
4
+ <%= csrf_meta_tags %>
5
+ <title><%= ( @title ? "#{@title} - " : "" ) + "#{app_name} Admin" %></title>
6
+
7
+ <%= stylesheet_link_tag 'admin/admin' %>
8
+ <%= yield :stylesheets %>
9
+ </head>
10
+
11
+ <body>
12
+ <%= yield %>
13
+ </body>
14
+ </html>
15
+
@@ -30,6 +30,10 @@ it:
30
30
  choose_a_file: "Carica"
31
31
  change: "Cambia"
32
32
  preview: "Anteprima"
33
+ signed_in: 'Login effettuato con successo.'
34
+ signed_out: 'Logout effettuato con successo.'
35
+ login_failed: 'Nome utente o password non validi.'
36
+
33
37
 
34
38
  form:
35
39
  correct_these_errors_and_retry: "Correggi questi errori e riprova"
@@ -65,4 +65,5 @@ it:
65
65
  category: "Categoria"
66
66
  post_code: "Codice postale"
67
67
  kind: "Tipo"
68
- state: "Provincia"
68
+ state: "Provincia"
69
+ password_confirmation: "Conferma Password"
@@ -3,6 +3,10 @@ Rails.application.routes.draw do
3
3
  mount Ckeditor::Engine => '/ckeditor'
4
4
 
5
5
  namespace :admin do
6
+ resources :sessions, :only => [:new, :create] do
7
+ match :destroy, :via => :delete, :on => :collection
8
+ end
9
+
6
10
  resources :positionables, :only => [:index] do
7
11
  post :sort, :on => :collection
8
12
  end
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = "fullstack-admin"
8
- s.version = "0.2.1"
8
+ s.version = "0.2.2"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["mcasimir"]
12
- s.date = "2012-11-04"
12
+ s.date = "2012-11-05"
13
13
  s.description = "Administration interface framework for fullstack"
14
14
  s.email = "maurizio.cas@gmail.com"
15
15
  s.extra_rdoc_files = [
@@ -939,6 +939,7 @@ Gem::Specification.new do |s|
939
939
  "app/assets/javascripts/support/plupload.js.coffee",
940
940
  "app/assets/javascripts/support/uploads.js.coffee",
941
941
  "app/assets/stylesheets/admin/base.css",
942
+ "app/assets/stylesheets/admin/login.css",
942
943
  "app/assets/stylesheets/support/ajax_loading.css",
943
944
  "app/assets/stylesheets/support/base.css",
944
945
  "app/assets/stylesheets/support/bootstrap-workarounds.css",
@@ -952,6 +953,7 @@ Gem::Specification.new do |s|
952
953
  "app/controllers/admin/responder.rb",
953
954
  "app/controllers/admin/scaffold_controller.rb",
954
955
  "app/controllers/admin/sessions_controller.rb",
956
+ "app/controllers/admin/subject_model_adapter.rb",
955
957
  "app/helpers/admin_form_helper.rb",
956
958
  "app/helpers/scaffold_helper.rb",
957
959
  "app/inputs/country_input.rb",
@@ -993,6 +995,7 @@ Gem::Specification.new do |s|
993
995
  "app/views/admin/base/new.html.erb",
994
996
  "app/views/admin/base/update.js.coffee",
995
997
  "app/views/admin/positionables/_collection.html.erb",
998
+ "app/views/admin/sessions/new.html.erb",
996
999
  "app/views/kaminari/_first_page.html.erb",
997
1000
  "app/views/kaminari/_gap.html.erb",
998
1001
  "app/views/kaminari/_last_page.html.erb",
@@ -1001,6 +1004,7 @@ Gem::Specification.new do |s|
1001
1004
  "app/views/kaminari/_paginator.html.erb",
1002
1005
  "app/views/kaminari/_prev_page.html.erb",
1003
1006
  "app/views/layouts/admin.html.erb",
1007
+ "app/views/layouts/login.html.erb",
1004
1008
  "config/initializers/formtastic_bootstrap_timeish_hack.rb",
1005
1009
  "config/locales/devise.en.yml",
1006
1010
  "config/locales/devise.views.en.yml",
@@ -61,7 +61,7 @@ module Fullstack
61
61
 
62
62
  # Group
63
63
  class Group < Entity
64
- attr_accessor :children, :name
64
+ attr_accessor :children, :name, :icon
65
65
 
66
66
  def initialize(name)
67
67
  @name = "#{name}"
@@ -128,6 +128,27 @@ module Fullstack
128
128
  end
129
129
 
130
130
  module_function :resources
131
+
132
+ def grouped_resources
133
+ if !@resource_groups
134
+ @resource_groups = {}
135
+ current_group = nil
136
+
137
+ resources.each do |rog|
138
+ if rog.type == :group
139
+ @resource_groups[rog] = []
140
+ current_group = rog
141
+ elsif current_group
142
+ @resource_groups[current_group] << rog
143
+ end
144
+ end
145
+ end
146
+ @resource_groups
147
+ end
148
+
149
+ module_function :grouped_resources
150
+
151
+
131
152
  end
132
153
  end
133
154
 
@@ -33,12 +33,13 @@ eos
33
33
 
34
34
 
35
35
  def users
36
+ generate "sorcery:install remember_me activity_logging brute_force_protection --model Superuser"
36
37
  generate "migration:from user"
37
38
  append_to_file "db/seeds.rb" do
38
39
  <<-eos
39
40
 
40
41
  if Rails.env.development?
41
- user = User.new( :email => "admin@example.com",
42
+ user = Superuser.new( :email => "admin@example.com",
42
43
  :password => "password" )
43
44
 
44
45
  user.skip_confirmation! if user.respond_to?(:skip_confirmation!)
@@ -64,7 +65,7 @@ eos
64
65
  eos
65
66
  route(src)
66
67
 
67
- route("\n devise_for :users\n")
68
+ #route("\n devise_for :users\n")
68
69
  end
69
70
 
70
71
  protected
@@ -13,7 +13,7 @@ class UserSubject < Checkin::Subject
13
13
  end
14
14
 
15
15
  role :administrator, :require => :logged_in, :alias => :admin do
16
- subject_model.has_role?(:administrator)
16
+ subject_model && subject_model.is_a?(Superuser)
17
17
  end
18
18
 
19
19
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: fullstack-admin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.1
4
+ version: 0.2.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-11-04 00:00:00.000000000 Z
12
+ date: 2012-11-05 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rails
@@ -1149,6 +1149,7 @@ files:
1149
1149
  - app/assets/javascripts/support/plupload.js.coffee
1150
1150
  - app/assets/javascripts/support/uploads.js.coffee
1151
1151
  - app/assets/stylesheets/admin/base.css
1152
+ - app/assets/stylesheets/admin/login.css
1152
1153
  - app/assets/stylesheets/support/ajax_loading.css
1153
1154
  - app/assets/stylesheets/support/base.css
1154
1155
  - app/assets/stylesheets/support/bootstrap-workarounds.css
@@ -1162,6 +1163,7 @@ files:
1162
1163
  - app/controllers/admin/responder.rb
1163
1164
  - app/controllers/admin/scaffold_controller.rb
1164
1165
  - app/controllers/admin/sessions_controller.rb
1166
+ - app/controllers/admin/subject_model_adapter.rb
1165
1167
  - app/helpers/admin_form_helper.rb
1166
1168
  - app/helpers/scaffold_helper.rb
1167
1169
  - app/inputs/country_input.rb
@@ -1203,6 +1205,7 @@ files:
1203
1205
  - app/views/admin/base/new.html.erb
1204
1206
  - app/views/admin/base/update.js.coffee
1205
1207
  - app/views/admin/positionables/_collection.html.erb
1208
+ - app/views/admin/sessions/new.html.erb
1206
1209
  - app/views/kaminari/_first_page.html.erb
1207
1210
  - app/views/kaminari/_gap.html.erb
1208
1211
  - app/views/kaminari/_last_page.html.erb
@@ -1211,6 +1214,7 @@ files:
1211
1214
  - app/views/kaminari/_paginator.html.erb
1212
1215
  - app/views/kaminari/_prev_page.html.erb
1213
1216
  - app/views/layouts/admin.html.erb
1217
+ - app/views/layouts/login.html.erb
1214
1218
  - config/initializers/formtastic_bootstrap_timeish_hack.rb
1215
1219
  - config/locales/devise.en.yml
1216
1220
  - config/locales/devise.views.en.yml
@@ -1291,7 +1295,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
1291
1295
  version: '0'
1292
1296
  segments:
1293
1297
  - 0
1294
- hash: -697068176308442679
1298
+ hash: 445393833660587370
1295
1299
  required_rubygems_version: !ruby/object:Gem::Requirement
1296
1300
  none: false
1297
1301
  requirements: