push_type_admin 0.2.1 → 0.3.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/push_type/assets.js.coffee +0 -3
  3. data/app/assets/stylesheets/push_type/admin.scss +29 -5
  4. data/app/controllers/push_type/admin_controller.rb +1 -0
  5. data/app/controllers/push_type/assets_controller.rb +25 -3
  6. data/app/controllers/push_type/nodes_controller.rb +34 -6
  7. data/app/helpers/push_type/assets_helper.rb +8 -0
  8. data/app/helpers/push_type/nodes_helper.rb +10 -0
  9. data/app/views/layouts/push_type/admin.html.erb +4 -11
  10. data/app/views/push_type/admin/info.html.haml +1 -1
  11. data/app/views/push_type/assets/_form.html.haml +3 -9
  12. data/app/views/push_type/assets/_form_submit.html.haml +13 -0
  13. data/app/views/push_type/assets/_meta_table.html.haml +15 -0
  14. data/app/views/push_type/assets/edit.html.haml +3 -0
  15. data/app/views/push_type/assets/index.html.haml +30 -30
  16. data/app/views/push_type/assets/trash.html.haml +35 -0
  17. data/app/views/push_type/nodes/_form_fields.html.haml +3 -29
  18. data/app/views/push_type/nodes/_form_submit.html.haml +24 -0
  19. data/app/views/push_type/nodes/_meta_table.html.haml +33 -8
  20. data/app/views/push_type/nodes/edit.html.haml +6 -2
  21. data/app/views/push_type/nodes/index.html.haml +50 -48
  22. data/app/views/push_type/nodes/trash.html.haml +48 -0
  23. data/app/views/push_type/users/_form.html.haml +3 -1
  24. data/config/routes.rb +16 -2
  25. data/lib/push_type/admin/engine.rb +32 -0
  26. data/test/controllers/push_type/assets_controller_test.rb +30 -0
  27. data/test/controllers/push_type/nodes_controller_test.rb +45 -4
  28. data/test/dummy/app/models/page.rb +1 -1
  29. data/test/dummy/app/views/nodes/page.html.erb +3 -1
  30. data/test/dummy/config/initializers/push_type.rb +1 -1
  31. data/test/dummy/config/secrets.yml +2 -2
  32. data/test/dummy/db/migrate/{20150209204345_create_push_type_users.push_type.rb → 20150222152455_create_push_type_users.push_type.rb} +0 -0
  33. data/test/dummy/db/migrate/{20150209204346_create_push_type_nodes.push_type.rb → 20150222152456_create_push_type_nodes.push_type.rb} +0 -0
  34. data/test/dummy/db/migrate/{20150209204347_create_push_type_node_hierarchies.push_type.rb → 20150222152457_create_push_type_node_hierarchies.push_type.rb} +0 -0
  35. data/test/dummy/db/migrate/{20150209204348_create_push_type_assets.push_type.rb → 20150222152458_create_push_type_assets.push_type.rb} +0 -0
  36. data/test/dummy/db/schema.rb +1 -1
  37. data/test/dummy/log/test.log +3552 -2380
  38. data/test/dummy/tmp/cache/assets/test/sass/2df752541ff0a319141a1cb83ca971d2ffd2d050/admin.scssc +0 -0
  39. data/test/dummy/tmp/cache/assets/test/sprockets/0e01ae9de0f3914c8317853aedd9ace4 +0 -0
  40. data/test/dummy/tmp/cache/assets/test/sprockets/30f88df6b769522639aee569e18ab005 +0 -0
  41. data/test/dummy/tmp/cache/assets/test/sprockets/3508023b6222e6b0876c773e38ec700d +0 -0
  42. data/test/dummy/tmp/cache/assets/test/sprockets/5f85da4ac11a64f005e2656b99be44ac +0 -0
  43. data/test/dummy/tmp/cache/assets/test/sprockets/8d531fdb806de0724337e0b80e253914 +0 -0
  44. data/test/dummy/tmp/cache/assets/test/sprockets/9c34afd1737e54e4eb06cc87ddf17b78 +0 -0
  45. data/test/dummy/tmp/cache/assets/test/sprockets/a93ec74f5cd7f26f8c663d4c481fe689 +0 -0
  46. data/test/dummy/tmp/cache/assets/test/sprockets/a9b8decaa2942e8913458c9f35da2693 +0 -0
  47. data/test/dummy/tmp/cache/assets/test/sprockets/c206fbd33f49ab785c6ddf4bcffd3b93 +0 -0
  48. data/test/dummy/tmp/cache/assets/test/sprockets/df90022afc0d52f16bc498be20a5967d +0 -0
  49. data/test/dummy/tmp/cache/assets/test/sprockets/e540a9dc1aa001f8c4cba7a6b00f69d2 +0 -0
  50. data/test/dummy/tmp/cache/assets/test/sprockets/edd24c59de6c00e4e68dbbdf2775984a +0 -0
  51. metadata +25 -17
  52. data/app/assets/images/push_type/pushtype-logo.png +0 -0
  53. data/test/dummy/tmp/cache/assets/test/sprockets/8da458b8df4bfbd9fb0eed86c36442d4 +0 -0
  54. data/test/dummy/tmp/cache/assets/test/sprockets/a010781c05d478892856f75c751d6ab4 +0 -0
@@ -2,7 +2,7 @@
2
2
  .large-8.columns
3
3
  .container
4
4
  .head
5
- .left.btn= link_to ficon(:previous), @node.parent ? push_type.node_nodes_path(@node.parent) : push_type.nodes_path
5
+ .left.btn= link_to ficon(:previous), node_back_link(@node)
6
6
  .title= yield :title
7
7
  .body.padded
8
8
  .row.title
@@ -30,34 +30,8 @@
30
30
  .large-4.columns
31
31
  .side-panel{ :'side-panel' => true }
32
32
  .padded
33
- .meta-table
34
- .meta-row{ ng: { class: 'publishDatesVisibility()' } }
35
- .key= ficon :calendar, 'Publish'
36
- .value.editable{ data: { dropdown: 'publish-date-fields' }} {{ publishedDates() }}
37
- #publish-date-fields.f-dropdown.content{ data: { dropdown: { content: true } } }
38
- .row.publish-date
39
- .columns
40
- = f.label :published_at
41
- = f.datetime_select :published_at, {}, :'node-datetime' => 'published_at'
42
- .row.publish-date
43
- .columns
44
- = f.label :published_to
45
- = f.datetime_select :published_to, include_blank: true, default: nil
46
- = render 'meta_table'
47
-
33
+ = render 'meta_table', f: f
48
34
  .submit.text-center
49
- = f.hidden_field :status, value: '{{ node.status }}'
50
- %button.button.radius.split{ ng: { class: 'saveButtonClass()' } }
51
- = ficon :check, "{{ saveButtonText() }}"
52
- %span{ data: { dropdown: 'publish-toggle', options: 'align:bottom' } }
53
- %ul#publish-toggle.f-dropdown.tiny{ data: { dropdown: { content: true } } }
54
- %li
55
- %a{ href: '#', node: { status: 'draft' } } Save as draft
56
- %li
57
- %a{ href: '#', node: { status: 'published' } } Save and publish
58
-
59
- - unless @node.new_record?
60
- .delete-link
61
- = link_to "Delete #{@node.type.underscore.humanize.downcase}", push_type.node_path(@node), method: 'delete', data: { confirm: { body: 'Deleted content will be moved to the trash. You can restore trashed content later.' } }
35
+ = render 'form_submit', f: f
62
36
 
63
37
 
@@ -0,0 +1,24 @@
1
+ - unless @node.trashed?
2
+ = f.hidden_field :status, value: '{{ node.status }}'
3
+ %button.button.radius.split{ ng: { class: 'saveButtonClass()' } }
4
+ = ficon :check, "{{ saveButtonText() }}"
5
+ %span{ data: { dropdown: 'publish-toggle', options: 'align:bottom' } }
6
+ %ul#publish-toggle.f-dropdown.tiny{ data: { dropdown: { content: true } } }
7
+ %li
8
+ %a{ href: '#', node: { status: 'draft' } } Save as draft
9
+ %li
10
+ %a{ href: '#', node: { status: 'published' } } Save and publish
11
+
12
+ - unless @node.new_record?
13
+ .text-button.alert
14
+ = link_to "Delete #{@node.type.underscore.humanize.downcase}", push_type.node_path(@node), method: 'delete', data: { confirm: { body: 'Deleted content, and all its child content, will be moved to the trash. You can restore trashed content later.' } }
15
+
16
+ - else
17
+ %button.button.radius.secondary= ficon :check, 'Save as trash'
18
+ .text-button.success
19
+ - if @node.orphan?
20
+ %span Orphan. Cannot be restored.
21
+ - else
22
+ = link_to "Restore #{ @node.type.underscore.humanize.downcase }", push_type.restore_node_path(@node), method: 'put'
23
+ .text-button.alert
24
+ = link_to "Permanently delete #{ @node.type.underscore.humanize.downcase }", push_type.node_path(@node), method: 'delete', data: { confirm: { body: 'This action cannot be undone. All child content will also be permanently deleted.' } }
@@ -1,8 +1,33 @@
1
- - if @node.creator
2
- .meta-row
3
- .key= ficon :torso, 'Author'
4
- .value= @node.creator.name
5
- - if @node.updater && @node.updater != @node.creator
6
- .meta-row
7
- .key= ficon :torso, 'Updated by'
8
- .value= @node.updater
1
+ .meta-table
2
+ - unless @node.trashed?
3
+ .meta-row{ ng: { class: 'publishDatesVisibility()' } }
4
+ .key= ficon :calendar, 'Publish'
5
+ .value.editable{ data: { dropdown: 'publish-date-fields' }} {{ publishedDates() }}
6
+ #publish-date-fields.f-dropdown.content{ data: { dropdown: { content: true } } }
7
+ .row.publish-date
8
+ .columns
9
+ = f.label :published_at
10
+ = f.datetime_select :published_at, {}, :'node-datetime' => 'published_at'
11
+ .row.publish-date
12
+ .columns
13
+ = f.label :published_to
14
+ = f.datetime_select :published_to, include_blank: true, default: nil
15
+
16
+ - if @node.creator
17
+ .meta-row
18
+ .key= ficon :torso, 'Author'
19
+ .value= @node.creator.name
20
+
21
+ - if @node.updater && @node.updater != @node.creator
22
+ .meta-row
23
+ .key= ficon :torso, 'Updated by'
24
+ .value= @node.updater.name
25
+
26
+ - if @node.trashed?
27
+ .meta-row
28
+ .key= ficon :calendar, 'Deleted'
29
+ .value= l @node.deleted_at, format: :short
30
+
31
+
32
+
33
+
@@ -1,6 +1,10 @@
1
1
  - title "Edit #{ @node.type.underscore.humanize.downcase }"
2
- - for node in @node.ancestors.reverse
3
- - breadcrumbs.add node.title, push_type.node_nodes_path(node)
2
+
3
+ - if @node.trashed?
4
+ - breadcrumbs.add 'Trash', push_type.trash_nodes_path
5
+ -else
6
+ - for node in @node.ancestors.reverse
7
+ - breadcrumbs.add node.title, push_type.node_nodes_path(node)
4
8
  - breadcrumbs.add @node.title, push_type.edit_node_path(@node)
5
9
 
6
10
  = form_for @node, url: push_type.node_path(@node) do |f|
@@ -4,54 +4,56 @@
4
4
  - for node in @parent.self_and_ancestors.reverse
5
5
  - breadcrumbs.add node.title, push_type.node_nodes_path(node)
6
6
 
7
- - if !@parent && @nodes.empty?
8
- .columns
9
- .empty-default
10
- %h2 No content
11
- %p A website without any content makes for a pretty poor user experience. Better sort this one out now and create your first page.
12
- = render 'new_node_button'
13
-
14
- - else
15
- .large-8.columns
16
- %section.container
17
- .head
18
- - if @parent
19
- .left.btn= link_to ficon(:previous), @parent.root? ? push_type.nodes_path : push_type.node_nodes_path(@parent.parent)
20
- .title= @parent.title
21
- - else
22
- .title Content
23
- .body
24
- - if @nodes.empty?
25
- .empty-default
26
- %h3 No content
7
+ .large-8.columns
8
+ %section.container
9
+ .head
10
+ - if @parent
11
+ .left.btn= link_to ficon(:previous), @parent.root? ? push_type.nodes_path : push_type.node_nodes_path(@parent.parent)
12
+ .title= @parent.title
13
+ - else
14
+ .title Content
15
+
16
+ .body
17
+ - if @nodes.empty?
18
+ .empty-default
19
+ %h3 No content
20
+ - if !@parent
21
+ %p A website without any content makes for a pretty poor user experience. Better sort this one out now and create your first page.
22
+ = render 'new_node_button'
23
+ - else
27
24
  %p There is no content in this section yet.
28
- - else
29
- %ul.node-list{ class: ('sortable' if !@parent || @parent.sortable?) }
30
- - for node in @nodes.all
31
- %li.node-list-item{ data: { id: node.id } }
32
- = link_to push_type.edit_node_path(node) do
33
- .title= node.title
34
- .meta
35
- %span.label.secondary.radius= node.type.to_s.underscore.humanize
36
- %span.label.radius{ class: node.published? ? 'success' : 'warning' }= node.status.titlecase
37
- %time Updated #{ time_ago_in_words node.updated_at } ago
25
+ - else
26
+ %ul.node-list{ class: ('sortable' if !@parent || @parent.sortable?) }
27
+ - for node in @nodes.all
28
+ %li.node-list-item{ data: { id: node.id } }
29
+ = link_to push_type.edit_node_path(node) do
30
+ .title= node.title
31
+ .meta
32
+ %span.label.secondary.radius= node.type.to_s.underscore.humanize
33
+ %span.label.radius{ class: node.published? ? 'success' : 'warning' }= node.status.titlecase
34
+ - if node.published?
35
+ %time Published #{ l node.published_at.to_date, format: :short }
38
36
  \-
39
- %span= pluralize node.child_ids.size, 'child'
40
- .buttons
41
- = link_to ficon(:magnifying_glass), '#'
42
- - unless node.child_nodes.empty?
43
- = link_to ficon(:folder), push_type.node_nodes_path(node)
44
- - if !@parent || @parent.sortable?
45
- .handle
37
+ %time Updated #{ time_ago_in_words node.updated_at } ago
38
+ \-
39
+ %span= pluralize node.children.not_trash.count, 'child'
40
+ .buttons
41
+ %span= ficon(:magnifying_glass)
42
+ = link_to ficon(:folder), push_type.node_nodes_path(node) unless node.child_nodes.empty?
43
+ - if !@parent || @parent.sortable?
44
+ .handle
46
45
 
47
- .large-4.columns
48
- .side-panel{ :'side-panel' => true }
49
- .padded
50
- .pagination-centered
51
- .pagination-stats
52
- = pluralize @nodes.total_count, 'item'
53
- - unless @nodes.total_count == 0
54
- \- Page #{ @nodes.current_page } of #{ @nodes.total_pages }
55
- = paginate @nodes
56
- .cta.text-center
57
- = render 'new_node_button'
46
+ .large-4.columns
47
+ .side-panel{ :'side-panel' => true }
48
+ .padded
49
+ .pagination-centered
50
+ .pagination-stats
51
+ = pluralize @nodes.total_count, 'item'
52
+ - unless @nodes.total_count == 0
53
+ \- Page #{ @nodes.current_page } of #{ @nodes.total_pages }
54
+ = paginate @nodes
55
+ .cta.text-center
56
+ = render 'new_node_button'
57
+ - if @nodes.current_page == 1
58
+ .text-button
59
+ = link_to ficon(:trash, 'Trash bin'), push_type.trash_nodes_path
@@ -0,0 +1,48 @@
1
+ - title 'Trash'
2
+
3
+ - breadcrumbs.add 'Trash', push_type.trash_nodes_path
4
+
5
+ - if @nodes.empty?
6
+ .columns
7
+ .empty-default
8
+ %h2 No trash
9
+ = link_to ficon(:arrow_left, 'Back to content'), push_type.nodes_path, class: 'button radius secondary'
10
+
11
+ - else
12
+ .large-8.columns
13
+ %section.container
14
+ .head
15
+ .left.btn= link_to ficon(:previous), push_type.nodes_path
16
+ .title Trash
17
+ .body
18
+ %ul.node-list
19
+ - for node in @nodes.all
20
+ %li.node-list-item{ data: { id: node.id } }
21
+ = link_to push_type.edit_node_path(node) do
22
+ .title= node.title
23
+ .meta
24
+ %span.label.secondary.radius= node.type.to_s.underscore.humanize
25
+ %span.label.radius.alert Trashed
26
+ - if node.orphan?
27
+ %span.label.radius.alert Orphan
28
+ %time Deleted #{ time_ago_in_words node.deleted_at } ago
29
+ \-
30
+ %span= pluralize node.children.count, 'child'
31
+ .buttons
32
+ - if node.orphan?
33
+ %span= ficon(:loop)
34
+ - else
35
+ = link_to ficon(:loop), push_type.restore_node_path(node), method: 'put', class: 'success'
36
+ = link_to ficon(:x), push_type.node_path(node), method: 'delete', class: 'alert', data: { confirm: { body: 'This action cannot be undone. All child content will also be permanently deleted.' } }
37
+
38
+ .large-4.columns
39
+ .side-panel{ :'side-panel' => true }
40
+ .padded
41
+ .pagination-centered
42
+ .pagination-stats
43
+ = pluralize @nodes.total_count, 'item'
44
+ - unless @nodes.total_count == 0
45
+ \- Page #{ @nodes.current_page } of #{ @nodes.total_pages }
46
+ = paginate @nodes
47
+ .cta.text-center
48
+ = link_to ficon(:trash, 'Empty trash'), push_type.trash_nodes_path, method: 'delete', class: 'button radius alert', data: { confirm: true }
@@ -27,6 +27,8 @@
27
27
  %button.button.success.radius= ficon :check, 'Create user'
28
28
  - else
29
29
  %button.button.radius= ficon :check, 'Update user'
30
- .delete-link= link_to 'Delete user', push_type.user_path(@user), method: 'delete', data: { confirm: true }
30
+ - unless @user == push_type_user
31
+ .text-button.alert
32
+ = link_to 'Delete user', push_type.user_path(@user), method: 'delete', data: { confirm: true }
31
33
 
32
34
 
@@ -1,12 +1,26 @@
1
1
  PushType::Core::Engine.routes.draw do
2
2
 
3
3
  resources :nodes, except: :show do
4
+ collection do
5
+ get 'trash'
6
+ delete 'trash' => 'nodes#empty'
7
+ end
8
+ member do
9
+ post 'position'
10
+ put 'restore'
11
+ end
4
12
  resources :nodes, only: [:index, :new, :create]
5
- post 'position', on: :member
6
13
  end
7
14
 
8
15
  resources :assets, except: :show, path: 'media' do
9
- post 'upload', on: :collection
16
+ collection do
17
+ post 'upload'
18
+ get 'trash'
19
+ delete 'trash' => 'assets#empty'
20
+ end
21
+ member do
22
+ put 'restore'
23
+ end
10
24
  end
11
25
 
12
26
  resources :users, except: :show
@@ -16,6 +16,38 @@ module PushType
16
16
  push_type/admin_assets.css
17
17
  push_type/admin_assets.js
18
18
  )
19
+
20
+ initializer 'push_type_admin.menus' do
21
+ PushType.menu :main do
22
+ element :ul
23
+ html_options class: 'inline-list'
24
+
25
+ item :content do
26
+ link { push_type.nodes_path }
27
+ active { request.fullpath.match %r{^#{ push_type.nodes_path }} }
28
+ end
29
+ item :media do
30
+ link { push_type.assets_path }
31
+ active { request.fullpath.match %r{^#{ push_type.assets_path }} }
32
+ end
33
+ item :users do
34
+ link { push_type.users_path }
35
+ active { request.fullpath.match %r{^#{ push_type.users_path }} }
36
+ end
37
+ end
38
+
39
+ PushType.menu :utility do
40
+ element :ul
41
+ html_options class: 'inline-list right'
42
+
43
+ item :info do
44
+ text { ficon(:info) }
45
+ link { push_type.info_path }
46
+ link_options data: { :'reveal-id' => 'reveal-ajax', :'reveal-ajax' => true }
47
+ end
48
+ end
49
+ end
50
+
19
51
  end
20
52
  end
21
53
  end
@@ -15,6 +15,16 @@ module PushType
15
15
  it { assigns[:assets].size.must_equal 5 }
16
16
  end
17
17
 
18
+ describe 'GET #trash' do
19
+ before do
20
+ 2.times { FactoryGirl.create :asset }
21
+ 3.times { FactoryGirl.create :asset, deleted_at: Time.zone.now }
22
+ get :trash
23
+ end
24
+ it { response.must_render_template 'trash' }
25
+ it { assigns[:assets].size.must_equal 3 }
26
+ end
27
+
18
28
  describe 'GET #new' do
19
29
  before { get :new }
20
30
  it { response.must_render_template 'new' }
@@ -84,5 +94,25 @@ module PushType
84
94
  it { asset.reload.must_be :trashed? }
85
95
  end
86
96
 
97
+ describe 'PUT #restore' do
98
+ before do
99
+ asset.trash!
100
+ put :restore, id: asset.id
101
+ end
102
+ it { response.must_respond_with :redirect }
103
+ it { flash[:notice].must_be :present? }
104
+ it { asset.reload.wont_be :trashed? }
105
+ end
106
+
107
+ describe 'DELETE #empty' do
108
+ before do
109
+ 3.times { FactoryGirl.create :asset, deleted_at: Time.zone.now }
110
+ delete :empty
111
+ end
112
+ it { response.must_respond_with :redirect }
113
+ it { flash[:notice].must_be :present? }
114
+ it { PushType::Asset.trashed.must_be :empty? }
115
+ end
116
+
87
117
  end
88
118
  end
@@ -15,6 +15,16 @@ module PushType
15
15
  it { assigns[:nodes].size.must_equal 5 }
16
16
  end
17
17
 
18
+ describe 'GET #trash' do
19
+ before do
20
+ 2.times { FactoryGirl.create :node }
21
+ 3.times { FactoryGirl.create :node, deleted_at: Time.zone.now }
22
+ get :trash
23
+ end
24
+ it { response.must_render_template 'trash' }
25
+ it { assigns[:nodes].size.must_equal 3 }
26
+ end
27
+
18
28
  describe 'GET #new' do
19
29
  before { get :new, kind: 'page' }
20
30
  it { response.must_render_template 'new' }
@@ -62,10 +72,21 @@ module PushType
62
72
  end
63
73
 
64
74
  describe 'DELETE #destroy' do
65
- before { delete :destroy, id: node.id }
66
- it { response.must_respond_with :redirect }
67
- it { flash[:notice].must_be :present? }
68
- it { node.reload.must_be :trashed? }
75
+ describe 'with untrashed node' do
76
+ before { delete :destroy, id: node.id }
77
+ it { response.must_respond_with :redirect }
78
+ it { flash[:notice].must_be :present? }
79
+ it { node.reload.must_be :trashed? }
80
+ end
81
+ describe 'with trashed node' do
82
+ before do
83
+ node.trash!
84
+ delete :destroy, id: node.id
85
+ end
86
+ it { response.must_respond_with :redirect }
87
+ it { flash[:notice].must_be :present? }
88
+ it { proc { node.reload }.must_raise ActiveRecord::RecordNotFound }
89
+ end
69
90
  end
70
91
 
71
92
  describe 'POST #position' do
@@ -97,5 +118,25 @@ module PushType
97
118
  end
98
119
  end
99
120
 
121
+ describe 'PUT #restore' do
122
+ before do
123
+ node.trash!
124
+ put :restore, id: node.id
125
+ end
126
+ it { response.must_respond_with :redirect }
127
+ it { flash[:notice].must_be :present? }
128
+ it { node.reload.wont_be :trashed? }
129
+ end
130
+
131
+ describe 'DELETE #empty' do
132
+ before do
133
+ 3.times { FactoryGirl.create :node, deleted_at: Time.zone.now }
134
+ delete :empty
135
+ end
136
+ it { response.must_respond_with :redirect }
137
+ it { flash[:notice].must_be :present? }
138
+ it { PushType::Node.trashed.must_be :empty? }
139
+ end
140
+
100
141
  end
101
142
  end