app_frame 0.5.5
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/.rspec +1 -0
- data/Gemfile +26 -0
- data/Gemfile.lock +181 -0
- data/LICENSE.txt +20 -0
- data/README.md +25 -0
- data/Rakefile +47 -0
- data/VERSION +1 -0
- data/app/assets/javascripts/app_frame/application.js +9 -0
- data/app/assets/stylesheets/app_frame/_devise.scss +41 -0
- data/app/assets/stylesheets/app_frame/_tree.scss +31 -0
- data/app/assets/stylesheets/app_frame/application.scss +73 -0
- data/app/controllers/app_frame/devise/confirmations_controller.rb +3 -0
- data/app/controllers/app_frame/devise/passwords_controller.rb +3 -0
- data/app/controllers/app_frame/devise/registrations_controller.rb +3 -0
- data/app/controllers/app_frame/devise/sessions_controller.rb +3 -0
- data/app/helpers/app_frame/bootstrap_helper.rb +36 -0
- data/app/helpers/app_frame/breadcrumb_helper.rb +18 -0
- data/app/helpers/app_frame/menu_helper.rb +43 -0
- data/app/helpers/app_frame/pagination_helper.rb +12 -0
- data/app/helpers/app_frame/resources_helper.rb +63 -0
- data/app/helpers/app_frame/scopes_helper.rb +94 -0
- data/app/helpers/app_frame/select_helper.rb +8 -0
- data/app/helpers/app_frame/tree_nav_helper.rb +18 -0
- data/app/models/menu.rb +44 -0
- data/app/models/settings.rb +4 -0
- data/app/views/app_frame/devise/confirmations/new.html.haml +8 -0
- data/app/views/app_frame/devise/passwords/edit.html.haml +13 -0
- data/app/views/app_frame/devise/passwords/new.html.haml +8 -0
- data/app/views/app_frame/devise/registrations/edit.html.haml +19 -0
- data/app/views/app_frame/devise/registrations/new.html.haml +10 -0
- data/app/views/app_frame/devise/sessions/new.html.haml +12 -0
- data/app/views/app_frame/devise/shared/_links.html.haml +14 -0
- data/app/views/app_frame/devise/unlocks/new.html.haml +9 -0
- data/app/views/application/_brand.html.haml +1 -0
- data/app/views/application/_breadcrumb.html.haml +12 -0
- data/app/views/application/_flashes.html.haml +2 -0
- data/app/views/application/_form.html.haml +2 -0
- data/app/views/application/_head.html.haml +6 -0
- data/app/views/application/_secondary_menu.html.haml +3 -0
- data/app/views/application/_show.html.haml +5 -0
- data/app/views/application/_sidebar.html.haml +12 -0
- data/app/views/application/_sub_menu.html.haml +0 -0
- data/app/views/application/_table.html.haml +3 -0
- data/app/views/application/_toolbar.html.haml +7 -0
- data/app/views/application/_top_menu.html.haml +1 -0
- data/app/views/application/edit.html.haml +8 -0
- data/app/views/application/index.html.haml +8 -0
- data/app/views/application/new.html.haml +7 -0
- data/app/views/application/show.html.haml +10 -0
- data/app/views/kaminari/app_frame/_first_page.html.haml +9 -0
- data/app/views/kaminari/app_frame/_gap.html.haml +8 -0
- data/app/views/kaminari/app_frame/_last_page.html.haml +9 -0
- data/app/views/kaminari/app_frame/_next_page.html.haml +9 -0
- data/app/views/kaminari/app_frame/_page.html.haml +10 -0
- data/app/views/kaminari/app_frame/_paginator.html.haml +19 -0
- data/app/views/kaminari/app_frame/_prev_page.html.haml +9 -0
- data/app/views/layouts/app_frame/default.html.haml +33 -0
- data/app/views/layouts/app_frame/devise.html.haml +17 -0
- data/app_frame.gemspec +162 -0
- data/lib/app_frame.rb +27 -0
- data/lib/app_frame/controller_methods.rb +111 -0
- data/lib/app_frame/view_methods.rb +19 -0
- data/lib/assets/images/.gitkeep +0 -0
- data/lib/assets/javascripts/anytime.js +3716 -0
- data/lib/assets/stylesheets/anytime.css +777 -0
- data/lib/engine.rb +197 -0
- data/spec/app_frame_spec.rb +7 -0
- data/spec/spec_helper.rb +12 -0
- metadata +423 -0
@@ -0,0 +1,36 @@
|
|
1
|
+
module AppFrame
|
2
|
+
module BootstrapHelper
|
3
|
+
def alert(message, type = nil)
|
4
|
+
message = close_button + message
|
5
|
+
content_tag(:div, message, :class => "alert alert-#{alert_type_map(type)}")
|
6
|
+
end
|
7
|
+
|
8
|
+
def close_button
|
9
|
+
content_tag(:a, "×".html_safe, :class => 'close', :data => { :dismiss => "alert" })
|
10
|
+
end
|
11
|
+
|
12
|
+
def alert_type_map(type)
|
13
|
+
map = {
|
14
|
+
:alert => 'error',
|
15
|
+
:notice => 'success'
|
16
|
+
}
|
17
|
+
|
18
|
+
map[type.to_sym] || type
|
19
|
+
end
|
20
|
+
|
21
|
+
def icon(key, options = {})
|
22
|
+
css_class = "icon-#{key}"
|
23
|
+
css_class += " icon-white" if options[:invert]
|
24
|
+
content_tag(:i, '', :class => css_class).html_safe
|
25
|
+
end
|
26
|
+
|
27
|
+
def page_header(title, &block)
|
28
|
+
content = "".html_safe
|
29
|
+
content += content_tag(:div, capture(&block), :class => 'page-actions') if block_given?
|
30
|
+
content += content_tag(:h1, title)
|
31
|
+
|
32
|
+
content_tag(:div, content, :class => 'page-header')
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AppFrame::BreadcrumbHelper
|
2
|
+
|
3
|
+
def breadcrumb(items = [])
|
4
|
+
|
5
|
+
yield items if block_given?
|
6
|
+
|
7
|
+
return unless items.any?
|
8
|
+
|
9
|
+
last = items.pop
|
10
|
+
|
11
|
+
result = items.map { |i| content_tag(:li, i + content_tag(:span, "/", :class => 'divider')) }
|
12
|
+
result << content_tag(:li, last, :class => 'active')
|
13
|
+
|
14
|
+
content_tag :ul, result.join().html_safe, :class => 'breadcrumb'
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module AppFrame
|
2
|
+
module MenuHelper
|
3
|
+
def menu_link(key, path = '#', options = {}, &block)
|
4
|
+
active = false
|
5
|
+
highlight = options.delete(:highlights_on) || /#{path}/
|
6
|
+
dropdown = options.delete(:dropdown)
|
7
|
+
|
8
|
+
Array(highlight).each do |regex|
|
9
|
+
active = true if request.path =~ regex
|
10
|
+
end
|
11
|
+
|
12
|
+
options[:class] ||= ""
|
13
|
+
options[:class] << ' active' if active
|
14
|
+
options[:class] << ' dropdown' if dropdown
|
15
|
+
options[:class] = options[:class].present? ? options[:class].strip : nil
|
16
|
+
|
17
|
+
key = t(:"menu.#{key}") if key.is_a?(Symbol)
|
18
|
+
|
19
|
+
a_class = dropdown ? 'dropdown-toggle' : nil
|
20
|
+
ul_class = dropdown ? 'dropdown-menu' : nil
|
21
|
+
|
22
|
+
content = link_to(key, path, :class => a_class)
|
23
|
+
content += content_tag(:ul, capture(&block), :class => ul_class) if block_given?
|
24
|
+
|
25
|
+
content_tag :li, content, options
|
26
|
+
end
|
27
|
+
|
28
|
+
def content_locale_switch(obj = nil)
|
29
|
+
return unless obj.respond_to?(:translated_locales)
|
30
|
+
|
31
|
+
current_locale = (params[:content_locale] || I18n.default_locale).to_sym
|
32
|
+
|
33
|
+
locales = I18n.available_locales.map do |s|
|
34
|
+
text = s.to_s.upcase
|
35
|
+
text << "*" if obj && !obj.translated_locales.include?(s)
|
36
|
+
content_tag :li, link_to(text, url_for(:content_locale => s)), :class => current_locale == s ? 'active' : nil
|
37
|
+
end
|
38
|
+
|
39
|
+
content_tag :ul, locales.join("").html_safe, :class => 'nav nav-pills'
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module AppFrame::PaginationHelper
|
2
|
+
|
3
|
+
def page_range
|
4
|
+
return nil if count == 0
|
5
|
+
|
6
|
+
range_start = (page - 1) * per_page + 1
|
7
|
+
range_end = (page) * per_page
|
8
|
+
range_end = count if range_end > count
|
9
|
+
|
10
|
+
"<div class='page-range'>Displaying <strong>#{range_start} - #{range_end}</strong> of <strong>#{count}</strong> in total</div>".html_safe
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module AppFrame::ResourcesHelper
|
2
|
+
|
3
|
+
def collection?
|
4
|
+
!!collection
|
5
|
+
rescue NameError
|
6
|
+
false
|
7
|
+
end
|
8
|
+
|
9
|
+
def resource?
|
10
|
+
!!resource
|
11
|
+
rescue NameError, ActiveRecord::RecordNotFound
|
12
|
+
false
|
13
|
+
end
|
14
|
+
|
15
|
+
def resource_name
|
16
|
+
if resource_class
|
17
|
+
resource_class.model_name.human
|
18
|
+
else
|
19
|
+
controller_name
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def parent?
|
24
|
+
controller.respond_to?(:parent?) && controller.send(:parent?)
|
25
|
+
end
|
26
|
+
|
27
|
+
def parent_resource_class
|
28
|
+
parent.class
|
29
|
+
end
|
30
|
+
|
31
|
+
def parent_resource_name
|
32
|
+
parent_resource_class.model_name.human
|
33
|
+
end
|
34
|
+
|
35
|
+
def controller_namespaces
|
36
|
+
result = controller.class.to_s.split('::')
|
37
|
+
result.pop
|
38
|
+
result.map(&:underscore)
|
39
|
+
end
|
40
|
+
|
41
|
+
def namespaced(string)
|
42
|
+
if controller_namespaces.any?
|
43
|
+
File.join(controller_namespaces, string)
|
44
|
+
else
|
45
|
+
string
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def resource_path_array
|
50
|
+
result = controller_namespaces
|
51
|
+
result << parent if parent?
|
52
|
+
result << resource
|
53
|
+
end
|
54
|
+
|
55
|
+
def parent_collection_url
|
56
|
+
return unless parent?
|
57
|
+
|
58
|
+
result = controller_namespaces
|
59
|
+
result << parent_resource_name.underscore.pluralize
|
60
|
+
polymorphic_url(result)
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module AppFrame::ScopesHelper
|
2
|
+
# return all scopes configured in the controller
|
3
|
+
def scopes
|
4
|
+
controller.class.scopes_configuration || {}
|
5
|
+
end
|
6
|
+
|
7
|
+
def default_scopes
|
8
|
+
@default_scopes ||= scopes.inject({}) do |result, element|
|
9
|
+
result[element.first] = element.last if element.last[:default] == true
|
10
|
+
result
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
# return only the boolean scopes
|
15
|
+
def boolean_scopes
|
16
|
+
@boolean_scopes ||= scopes.inject({}) do |result, element|
|
17
|
+
result[element.first] = element.last if element.last[:type] == :boolean
|
18
|
+
result
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# return only the filter (non boolean) scopes
|
23
|
+
def filter_scopes
|
24
|
+
@filter_scopes ||= scopes.inject({}) do |result, element|
|
25
|
+
result[element.first] = element.last if element.last[:type] != :boolean
|
26
|
+
result
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_boolean_scopes
|
31
|
+
@current_boolean_scopes ||= current_scopes.inject({}) do |result, element|
|
32
|
+
result[element.first] = element.last if boolean_scopes.keys.include?(element.first)
|
33
|
+
result
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def current_filter_scopes
|
38
|
+
@current_filter_scopes ||= current_scopes.inject({}) do |result, element|
|
39
|
+
result[element.first] = element.last if filter_scopes.keys.include?(element.first)
|
40
|
+
result
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# create a link to a scope (or no scopes), wrapped in a list item
|
45
|
+
def scope_link(scope = nil)
|
46
|
+
if scope
|
47
|
+
as = scope[:as]
|
48
|
+
name = as.to_s.humanize
|
49
|
+
else
|
50
|
+
as = nil
|
51
|
+
name = "All"
|
52
|
+
end
|
53
|
+
|
54
|
+
link = current_filter_scopes.dup
|
55
|
+
|
56
|
+
if as
|
57
|
+
unless scope[:default]
|
58
|
+
link[as] = true
|
59
|
+
default_scopes.each { |dn, ds| link[ds[:as]] = false }
|
60
|
+
end
|
61
|
+
|
62
|
+
active = current_boolean_scopes.keys.include?(as)
|
63
|
+
else
|
64
|
+
active = current_boolean_scopes.empty?
|
65
|
+
end
|
66
|
+
|
67
|
+
# name = "#{name}: #{scope_count(scope)}"
|
68
|
+
|
69
|
+
content_tag(:li, link_to(name, link), :class => active ? 'active' : nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
def scope_count(scope)
|
73
|
+
chain = controller.end_of_association_chain
|
74
|
+
chain = chain.send(scope) if scope
|
75
|
+
chain.count
|
76
|
+
end
|
77
|
+
|
78
|
+
# craete a navigation for all the boolean scopes
|
79
|
+
def scope_nav
|
80
|
+
return unless boolean_scopes.count > 0
|
81
|
+
|
82
|
+
if default_scopes.any?
|
83
|
+
result = []
|
84
|
+
else
|
85
|
+
result = [scope_link]
|
86
|
+
end
|
87
|
+
|
88
|
+
boolean_scopes.each do |name, s|
|
89
|
+
result << scope_link(s)
|
90
|
+
end
|
91
|
+
|
92
|
+
content_tag :ul, result.join("\n").html_safe, :class => 'nav nav-pills'
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module AppFrame::TreeNavHelper
|
2
|
+
def tree_nav(node)
|
3
|
+
result = []
|
4
|
+
result << tree_nav_link("↑", left_admin_section_page_path(parent, node), node.left_sibling)
|
5
|
+
result << tree_nav_link("↓", right_admin_section_page_path(parent, node), node.right_sibling)
|
6
|
+
result << tree_nav_link("→", down_admin_section_page_path(parent, node), node.left_sibling)
|
7
|
+
result << tree_nav_link("←", up_admin_section_page_path(parent, node), node.parent)
|
8
|
+
result << link_to("Edit", edit_admin_section_page_path(parent, node))
|
9
|
+
yield result if block_given?
|
10
|
+
result << link_to("Delete", admin_section_page_path(parent, node), :method => :delete)
|
11
|
+
content_tag :span, result.join('').html_safe, :class => 'tree_nav'
|
12
|
+
end
|
13
|
+
|
14
|
+
def tree_nav_link(text, link, condition = true)
|
15
|
+
condition ? link_to(text.html_safe, link) : content_tag(:span, text.html_safe)
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
data/app/models/menu.rb
ADDED
@@ -0,0 +1,44 @@
|
|
1
|
+
class MenuItem
|
2
|
+
attr_reader :name, :path, :namespace
|
3
|
+
|
4
|
+
def initialize(namespace, path)
|
5
|
+
@namespace = namespace
|
6
|
+
@path = path
|
7
|
+
@name = path.humanize
|
8
|
+
end
|
9
|
+
|
10
|
+
def url
|
11
|
+
"/#{namespace}/#{path}"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
|
16
|
+
class Menu
|
17
|
+
attr_reader :namespace
|
18
|
+
|
19
|
+
def initialize(namespace)
|
20
|
+
@namespace = namespace
|
21
|
+
end
|
22
|
+
|
23
|
+
def routes
|
24
|
+
@routes ||= Rails.application.routes.routes
|
25
|
+
end
|
26
|
+
|
27
|
+
def menu_routes
|
28
|
+
@menu_routes ||= routes.select { |r| r.defaults[:action] == 'index' }
|
29
|
+
end
|
30
|
+
|
31
|
+
def specs
|
32
|
+
@specs ||= menu_routes.map { |r| r.path.spec }.uniq
|
33
|
+
end
|
34
|
+
|
35
|
+
def menu_items
|
36
|
+
@menu_items = []
|
37
|
+
specs.each do |s|
|
38
|
+
matches = /\/#{@namespace}\/(\w*)/.match(s.to_s)
|
39
|
+
@menu_items << matches[1] if matches
|
40
|
+
end
|
41
|
+
@menu_items.uniq.map { |i| MenuItem.new(@namespace, i) }
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
@@ -0,0 +1,8 @@
|
|
1
|
+
= page_header "Resend confirmation instructions"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => confirmation_path(resource_name), :html => { :method => :post }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true
|
7
|
+
|
8
|
+
= f.button :submit, "Resend confirmation instructions", :class => 'btn'
|
@@ -0,0 +1,13 @@
|
|
1
|
+
= page_header "Change your password"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :put }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :reset_password_token, :as => :hidden
|
7
|
+
= f.full_error :reset_password_token
|
8
|
+
|
9
|
+
= f.input :password, :label => "New password", :required => true
|
10
|
+
= f.input :password_confirmation, :label => "Confirm your new password", :required => true
|
11
|
+
|
12
|
+
= f.button :submit, "Change my password"
|
13
|
+
|
@@ -0,0 +1,8 @@
|
|
1
|
+
=page_header "Forgot your password?"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => password_path(resource_name), :html => { :method => :post }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true
|
7
|
+
|
8
|
+
= f.button :submit, "Send", :class => 'btn'
|
@@ -0,0 +1,19 @@
|
|
1
|
+
= page_header "Edit #{resource_name.to_s.humanize}"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name), :html => { :method => :put }) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true, :autofocus => true
|
7
|
+
= f.input :password, :hint => "leave it blank if you don't want to change it", :required => false
|
8
|
+
= f.input :password_confirmation, :required => false
|
9
|
+
= f.input :current_password, :hint => "we need your current password to confirm your changes", :required => true
|
10
|
+
|
11
|
+
= f.button :submit, "Update", :class => 'btn'
|
12
|
+
|
13
|
+
%h3 Cancel my account<
|
14
|
+
|
15
|
+
%p
|
16
|
+
Unhappy?
|
17
|
+
= link_to "Cancel my account", registration_path(resource_name), :confirm => "Are you sure?", :method => :delete
|
18
|
+
|
19
|
+
= link_to "Back", :back, :class => 'btn'
|
@@ -0,0 +1,10 @@
|
|
1
|
+
= page_header "Sign up"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => registration_path(resource_name)) do |f|
|
4
|
+
= f.error_notification
|
5
|
+
|
6
|
+
= f.input :email, :required => true, :autofocus => true
|
7
|
+
= f.input :password, :required => true
|
8
|
+
= f.input :password_confirmation, :required => true
|
9
|
+
|
10
|
+
= f.button :submit, "Sign up", :class => 'btn'
|
@@ -0,0 +1,12 @@
|
|
1
|
+
=page_header "Sign in"
|
2
|
+
|
3
|
+
= simple_form_for(resource, :as => resource_name, :url => session_path(resource_name)) do |f|
|
4
|
+
= f.input :email, :required => false, :autofocus => true
|
5
|
+
= f.input :password, :required => false
|
6
|
+
|
7
|
+
- if devise_mapping.rememberable?
|
8
|
+
= f.label :remember_me, :class => 'checkbox' do
|
9
|
+
= f.check_box :remember_me
|
10
|
+
Remember me
|
11
|
+
|
12
|
+
= f.button :submit, "Sign in", :class => 'btn'
|