push_type_admin 0.2.1 → 0.3.1

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.
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