integral 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (172) hide show
  1. checksums.yaml +5 -5
  2. data/README.md +4 -3
  3. data/app/assets/javascripts/integral/backend.js +86 -2
  4. data/app/assets/javascripts/integral/support/character_counter.js +13 -8
  5. data/app/assets/javascripts/integral/support/list.coffee +1 -0
  6. data/app/assets/javascripts/integral/support/record_selector.coffee +2 -0
  7. data/app/assets/javascripts/integral/support/slug_generator.coffee +1 -0
  8. data/app/assets/stylesheets/integral/backend.sass +34 -23
  9. data/app/assets/stylesheets/integral/backend/_foundation_settings.scss +2 -2
  10. data/app/assets/stylesheets/integral/backend/dashboard-layout.scss +27 -63
  11. data/app/assets/stylesheets/integral/backend/devise.sass +2 -3
  12. data/app/assets/stylesheets/integral/backend/materialize-tags.sass +1 -0
  13. data/app/assets/stylesheets/integral/backend/modules/dropdown_pane_notifications.scss +150 -0
  14. data/app/assets/stylesheets/integral/backend/modules/dropdown_pane_profile.scss +59 -0
  15. data/app/assets/stylesheets/integral/backend/shared.sass +41 -2
  16. data/app/assets/stylesheets/integral/frontend/layout.sass +10 -0
  17. data/app/assets/stylesheets/integral/support/media-query-indicator.sass +4 -4
  18. data/app/controllers/integral/backend/activities_controller.rb +21 -27
  19. data/app/controllers/integral/backend/base_controller.rb +87 -37
  20. data/app/controllers/integral/backend/images_controller.rb +26 -8
  21. data/app/controllers/integral/backend/lists_controller.rb +2 -14
  22. data/app/controllers/integral/backend/notification_subscriptions_controller.rb +23 -0
  23. data/app/controllers/integral/backend/pages_controller.rb +0 -4
  24. data/app/controllers/integral/backend/posts_controller.rb +0 -4
  25. data/app/controllers/integral/backend/settings_controller.rb +4 -0
  26. data/app/controllers/integral/backend/static_pages_controller.rb +6 -0
  27. data/app/controllers/integral/backend/users_controller.rb +43 -24
  28. data/app/controllers/integral/blog_controller.rb +12 -0
  29. data/app/controllers/integral/categories_controller.rb +17 -3
  30. data/app/controllers/integral/tags_controller.rb +5 -2
  31. data/app/decorators/integral/base_decorator.rb +16 -0
  32. data/app/decorators/integral/image_decorator.rb +2 -2
  33. data/app/decorators/integral/list_decorator.rb +1 -13
  34. data/app/decorators/integral/notification/notification_decorator.rb +74 -0
  35. data/app/decorators/integral/page_decorator.rb +1 -13
  36. data/app/decorators/integral/post_decorator.rb +1 -2
  37. data/app/decorators/integral/user_decorator.rb +1 -13
  38. data/app/decorators/integral/version_decorator.rb +8 -4
  39. data/app/helpers/integral/backend/base_helper.rb +97 -31
  40. data/app/jobs/integral/application_job.rb +1 -0
  41. data/app/jobs/integral/newsletter_signup_job.rb +0 -2
  42. data/app/mailers/integral/devise_mailer.rb +6 -0
  43. data/app/models/concerns/integral/notification/subscribable.rb +67 -0
  44. data/app/models/integral/application_record.rb +9 -0
  45. data/app/models/integral/category.rb +9 -0
  46. data/app/models/integral/image.rb +40 -3
  47. data/app/models/integral/list.rb +10 -2
  48. data/app/models/integral/list_item.rb +14 -14
  49. data/app/models/integral/list_item_connection.rb +6 -0
  50. data/app/models/integral/notification/notification.rb +28 -0
  51. data/app/models/integral/notification/subscription.rb +14 -0
  52. data/app/models/integral/page.rb +15 -8
  53. data/app/models/integral/post.rb +11 -13
  54. data/app/models/integral/user.rb +45 -2
  55. data/app/policies/integral/base_policy.rb +7 -12
  56. data/app/policies/integral/page_policy.rb +1 -0
  57. data/app/policies/integral/version_policy.rb +0 -8
  58. data/app/views/devise/invitations/edit.haml +1 -4
  59. data/app/views/devise/mailer/invitation_instructions.inky-haml +20 -0
  60. data/app/views/integral/backend/activities/grid/_dropdown_actions.haml +1 -0
  61. data/app/views/integral/backend/activities/grid/_row_content.haml +13 -0
  62. data/app/views/integral/backend/activities/index.haml +7 -13
  63. data/app/views/integral/backend/activities/shared/_grid.haml +35 -20
  64. data/app/views/integral/backend/activities/shared/index.haml +12 -12
  65. data/app/views/integral/backend/activities/shared/show.haml +7 -7
  66. data/app/views/integral/backend/activities/show.haml +1 -1
  67. data/app/views/integral/backend/categories/_modal.haml +2 -3
  68. data/app/views/integral/backend/images/_form.haml +13 -25
  69. data/app/views/integral/backend/images/edit.haml +1 -9
  70. data/app/views/integral/backend/images/grid/_dropdown_actions.haml +5 -0
  71. data/app/views/integral/backend/images/grid/_row_content.haml +5 -0
  72. data/app/views/integral/backend/images/index.haml +11 -17
  73. data/app/views/integral/backend/images/list.haml +11 -0
  74. data/app/views/integral/backend/images/show.haml +26 -0
  75. data/app/views/integral/backend/lists/_form.haml +6 -19
  76. data/app/views/integral/backend/lists/_item_modal.haml +3 -3
  77. data/app/views/integral/backend/lists/_manager.haml +11 -13
  78. data/app/views/integral/backend/lists/edit.haml +6 -20
  79. data/app/views/integral/backend/lists/grid/_dropdown_actions.haml +9 -0
  80. data/app/views/integral/backend/lists/grid/_row_content.haml +3 -0
  81. data/app/views/integral/backend/lists/index.haml +11 -17
  82. data/app/views/integral/backend/lists/list.haml +11 -0
  83. data/app/views/integral/backend/lists/show.haml +30 -0
  84. data/app/views/integral/backend/notifications/_notification.haml +21 -0
  85. data/app/views/integral/backend/pages/_form.haml +19 -43
  86. data/app/views/integral/backend/pages/edit.haml +4 -12
  87. data/app/views/integral/backend/pages/grid/_dropdown_actions.haml +11 -0
  88. data/app/views/integral/backend/pages/grid/_row_content.haml +5 -0
  89. data/app/views/integral/backend/pages/index.haml +6 -6
  90. data/app/views/integral/backend/pages/list.haml +12 -19
  91. data/app/views/integral/backend/pages/show.haml +19 -35
  92. data/app/views/integral/backend/posts/_form.haml +18 -56
  93. data/app/views/integral/backend/posts/edit.haml +4 -14
  94. data/app/views/integral/backend/posts/grid/_dropdown_actions.haml +10 -0
  95. data/app/views/integral/backend/posts/grid/_row_content.haml +6 -0
  96. data/app/views/integral/backend/posts/index.haml +6 -6
  97. data/app/views/integral/backend/posts/list.haml +11 -18
  98. data/app/views/integral/backend/posts/new.haml +0 -1
  99. data/app/views/integral/backend/posts/show.haml +18 -41
  100. data/app/views/integral/backend/shared/_breadcrumbs.haml +7 -4
  101. data/app/views/integral/backend/shared/_image_preview.haml +10 -3
  102. data/app/views/integral/backend/shared/_image_selector.haml +1 -1
  103. data/app/views/integral/backend/shared/_notification_subscription_toggle.haml +22 -0
  104. data/app/views/integral/backend/shared/action_bar/_index.haml +9 -0
  105. data/app/views/integral/backend/shared/action_bar/_show.haml +3 -0
  106. data/app/views/integral/backend/shared/cards/_at_a_glance.haml +3 -3
  107. data/app/views/integral/backend/shared/cards/_categories.haml +27 -28
  108. data/app/views/integral/backend/shared/cards/_object.haml +1 -1
  109. data/app/views/integral/backend/shared/cards/_recent_activity.haml +12 -12
  110. data/app/views/integral/backend/shared/cards/_recent_resources.haml +17 -0
  111. data/app/views/integral/backend/shared/cards/_top_post_authors.haml +14 -15
  112. data/app/views/integral/backend/shared/cards/_welcome.haml +24 -25
  113. data/app/views/integral/backend/shared/{_empty_grid.haml → grid/_empty.haml} +0 -0
  114. data/app/views/integral/backend/shared/grid/_form.haml +9 -0
  115. data/app/views/integral/backend/shared/grid/_grid.haml +21 -0
  116. data/app/views/integral/backend/shared/{_pagination.haml → grid/_pagination.haml} +0 -0
  117. data/app/views/integral/backend/shared/grid/_row_layout.haml +8 -0
  118. data/app/views/integral/backend/shared/record_selector/_collection.haml +1 -0
  119. data/app/views/integral/backend/shared/record_selector/_modal.haml +9 -10
  120. data/app/views/integral/backend/static_pages/dashboard.haml +6 -7
  121. data/app/views/integral/backend/users/_form.haml +34 -46
  122. data/app/views/integral/backend/users/grid/_dropdown_actions.haml +17 -0
  123. data/app/views/integral/backend/users/grid/_row_content.haml +8 -0
  124. data/app/views/integral/backend/users/index.haml +6 -6
  125. data/app/views/integral/backend/users/list.haml +10 -16
  126. data/app/views/integral/backend/users/show.haml +10 -1
  127. data/app/views/integral/categories/show.haml +3 -3
  128. data/app/views/integral/posts/_article_footer.haml +1 -1
  129. data/app/views/integral/posts/_card.haml +1 -1
  130. data/app/views/integral/posts/_most_read_section.haml +1 -1
  131. data/app/views/integral/posts/_post.haml +1 -1
  132. data/app/views/integral/posts/templates/default.haml +1 -1
  133. data/app/views/integral/shared/sidebar/_item.haml +1 -1
  134. data/app/views/layouts/integral/backend.html.haml +24 -5
  135. data/app/views/layouts/integral/backend/_create_dropdown.haml +1 -30
  136. data/app/views/layouts/integral/backend/_main_menu_items.haml +1 -101
  137. data/config/initializers/devise.rb +1 -1
  138. data/config/locales/en.yml +60 -2
  139. data/config/routes.rb +2 -0
  140. data/db/migrate/20200407022636_create_integral_notifications.rb +25 -0
  141. data/db/migrate/20200421223602_add_status_to_integral_users.rb +5 -0
  142. data/db/seeds.rb +15 -11
  143. data/lib/integral.rb +1 -0
  144. data/lib/integral/acts_as_integral.rb +115 -0
  145. data/lib/integral/acts_as_listable.rb +1 -1
  146. data/lib/integral/engine.rb +9 -0
  147. data/lib/integral/grids/activities_grid.rb +0 -1
  148. data/lib/integral/grids/lists_grid.rb +1 -0
  149. data/lib/integral/grids/posts_grid.rb +5 -1
  150. data/lib/integral/grids/users_grid.rb +5 -0
  151. data/lib/integral/list_renderer.rb +5 -1
  152. data/lib/integral/router.rb +20 -4
  153. data/lib/integral/version.rb +1 -1
  154. data/spec/factories.rb +15 -1
  155. metadata +45 -39
  156. data/app/decorators/integral/category_version_decorator.rb +0 -7
  157. data/app/decorators/integral/image_version_decorator.rb +0 -7
  158. data/app/decorators/integral/list_version_decorator.rb +0 -7
  159. data/app/decorators/integral/page_version_decorator.rb +0 -7
  160. data/app/decorators/integral/post_version_decorator.rb +0 -7
  161. data/app/decorators/integral/user_version_decorator.rb +0 -7
  162. data/app/views/devise/mailer/invitation_instructions.html.erb +0 -13
  163. data/app/views/integral/backend/activities/_grid.haml +0 -22
  164. data/app/views/integral/backend/images/_grid.haml +0 -16
  165. data/app/views/integral/backend/lists/_grid.haml +0 -14
  166. data/app/views/integral/backend/pages/_grid.haml +0 -46
  167. data/app/views/integral/backend/posts/_grid.haml +0 -51
  168. data/app/views/integral/backend/shared/_grid.haml +0 -18
  169. data/app/views/integral/backend/shared/cards/_recent_pages.haml +0 -19
  170. data/app/views/integral/backend/shared/cards/_recent_posts.haml +0 -18
  171. data/app/views/integral/backend/shared/cards/_recent_users.haml +0 -19
  172. data/app/views/integral/backend/users/_grid.haml +0 -36
@@ -1,101 +1 @@
1
- %li
2
- = link_to backend_dashboard_url do
3
- = icon('home')
4
- %span= t('integral.navigation.dashboard')
5
-
6
- - if policy(Integral::Page).index?
7
- %li
8
- = link_to backend_pages_url do
9
- = icon('file')
10
- %span= t('integral.navigation.pages')
11
- %ul
12
- %li= t('integral.navigation.pages')
13
- %li
14
- = link_to backend_pages_url do
15
- %span= t('integral.navigation.dashboard')
16
-
17
- - if policy(Integral::Page).create?
18
- %li
19
- = link_to new_backend_page_url do
20
- %span= t('integral.actions.create')
21
- %li
22
- = link_to list_backend_pages_url do
23
- %span= t('integral.navigation.listing')
24
-
25
-
26
- - if policy(Integral::Post).index? && Integral.blog_enabled?
27
- %li
28
- = link_to backend_posts_url do
29
- = icon('rss')
30
- %span= t('integral.navigation.posts')
31
- %ul
32
- %li= t('integral.navigation.posts')
33
- %li
34
- = link_to backend_posts_url do
35
- %span= t('integral.navigation.dashboard')
36
- - if policy(Integral::Post).create?
37
- %li
38
- = link_to new_backend_post_url do
39
- %span= t('integral.actions.create')
40
- %li
41
- = link_to list_backend_posts_url do
42
- %span= t('integral.navigation.listing')
43
-
44
- - if policy(Integral::List).index?
45
- %li
46
- = link_to backend_lists_url do
47
- = icon('list')
48
- %span= t('integral.navigation.lists')
49
- %ul
50
- %li= t('integral.navigation.lists')
51
- %li
52
- = link_to backend_lists_url do
53
- %span= t('integral.navigation.dashboard')
54
- - if policy(Integral::List).create?
55
- %li
56
- = link_to new_backend_list_url do
57
- %span= t('integral.actions.create')
58
-
59
- - if policy(Integral::Image).manager?
60
- %li
61
- = link_to backend_img_index_url do
62
- = icon('picture-o')
63
- %span= t('integral.navigation.images')
64
- %ul
65
- %li= t('integral.navigation.images')
66
- %li
67
- = link_to backend_img_index_url do
68
- %span= t('integral.navigation.dashboard')
69
- - if policy(Integral::Image).create?
70
- %li
71
- = link_to new_backend_img_url do
72
- %span= t('integral.actions.create')
73
-
74
- - if policy(current_user).manager?
75
- %li
76
- = link_to backend_users_url do
77
- = icon('users')
78
- %span=t('integral.navigation.users')
79
- %ul
80
- %li= t('integral.navigation.users')
81
- %li
82
- = link_to backend_users_url do
83
- %span= t('integral.navigation.dashboard')
84
- %li
85
- = link_to new_backend_user_url do
86
- %span= t('integral.actions.create')
87
- %li
88
- = link_to list_backend_users_url do
89
- %span= t('integral.navigation.listing')
90
-
91
- - if policy(Integral::Version).manager?
92
- %li
93
- = link_to backend_activities_url do
94
- = icon('crosshairs')
95
- %span=t('integral.navigation.activities')
96
-
97
- - if current_user.admin?
98
- %li
99
- = link_to backend_settings_url do
100
- = icon('cog')
101
- %span=t('integral.navigation.settings')
1
+ = render_main_menu
@@ -13,7 +13,7 @@ Devise.setup do |config|
13
13
  config.mailer_sender = Proc.new { "#{Integral::Settings.website_title} <#{Integral::Settings.contact_email}>" }
14
14
 
15
15
  # Configure the class responsible to send e-mails.
16
- # config.mailer = 'Devise::Mailer'
16
+ config.mailer = 'Integral::DeviseMailer'
17
17
 
18
18
  # ==> ORM configuration
19
19
  # Load and configure the ORM. Supports :active_record (default) and
@@ -15,6 +15,44 @@ en:
15
15
  default: "%A, %d %B %Y"
16
16
  long: "%H:%M %d/%m/%Y"
17
17
 
18
+ datetime:
19
+ distance_in_words:
20
+ short:
21
+ half_a_minute: "30s"
22
+ less_than_x_seconds:
23
+ one: "1s"
24
+ other: "%{count}s"
25
+ x_seconds:
26
+ one: "1s"
27
+ other: "%{count}s"
28
+ less_than_x_minutes:
29
+ one: "~1m"
30
+ other: "~%{count}m"
31
+ x_minutes:
32
+ one: "1m"
33
+ other: "%{count}m"
34
+ about_x_hours:
35
+ one: "~1h"
36
+ other: "~%{count}h "
37
+ x_days:
38
+ one: "1d"
39
+ other: "%{count}d"
40
+ about_x_months:
41
+ one: "~1mn"
42
+ other: "~%{count}mn"
43
+ x_months:
44
+ one: "1mn"
45
+ other: "%{count}mn"
46
+ about_x_years:
47
+ one: "~1y"
48
+ other: "~%{count}y"
49
+ over_x_years:
50
+ one: "1y"
51
+ other: "%{count}y"
52
+ almost_x_years:
53
+ one: "~1y"
54
+ other: "~%{count}y"
55
+
18
56
  errors:
19
57
  generic: Unfortunately there was a problem. Please try again later.
20
58
  unauthorized: You are not authorized to perform this action.
@@ -55,6 +93,8 @@ en:
55
93
  confirmation:
56
94
  deletion: If you delete this item it will be gone forever. Are you sure you want to proceed?
57
95
  clone: Are you sure you want to clone this item?
96
+ block: Are you sure you want to block this user?
97
+ unblock: Are you sure you want to unblock this user?
58
98
  new_page: Add new page
59
99
  new_post: Add new post
60
100
  edit_profile: Edit profile
@@ -63,6 +103,8 @@ en:
63
103
  view_main_site: View main site
64
104
  view_on_site: View on site
65
105
  publish: Publish
106
+ block: Block
107
+ unblock: Unblock
66
108
  create: Create
67
109
  clone: Clone
68
110
  save: Save
@@ -95,6 +137,9 @@ en:
95
137
  select_image: Select Image..
96
138
  select_type: Select Type..
97
139
  unsaved_changes: Changes that you made may not be saved.
140
+ tooltips:
141
+ unsubscribe: Unsubscribe from notifications.
142
+ subscribe: Subscribe to notifications.
98
143
  placeholders:
99
144
  description: Description
100
145
  title: Title
@@ -102,6 +147,9 @@ en:
102
147
  draft: Draft
103
148
  published: Published
104
149
  archived: Archived
150
+ blocked: Blocked
151
+ pending: Pending
152
+ active: Active
105
153
  records:
106
154
  attributes:
107
155
  category: Category
@@ -143,7 +191,7 @@ en:
143
191
  actions: Actions
144
192
  activity: Activity
145
193
  activity_log: Activity Log
146
- profile: My Profile
194
+ profile: My profile
147
195
  quick_links: Quick Links
148
196
  activities: Activities
149
197
  home: Home
@@ -161,7 +209,7 @@ en:
161
209
  new: New
162
210
  edit: Edit
163
211
  log_out: Logout
164
- my_account: Account & Profile
212
+ my_account: Account & profile
165
213
  settings: Settings
166
214
  listing: Listing
167
215
 
@@ -368,6 +416,8 @@ en:
368
416
  title: 'Image listing'
369
417
  no_images_available: 'No images available. Try uploading some!'
370
418
  new_image: 'New image'
419
+ edit:
420
+ title: Edit Image
371
421
  users:
372
422
  available_locales:
373
423
  en: English
@@ -424,6 +474,8 @@ en:
424
474
  list:
425
475
  title: This is only used for internal reference.
426
476
  description: This is only used for internal reference.
477
+ user:
478
+ notify_me: Subscribe to be notified whenever there is a change to any resource. Otherwise be notified about followed resources only.
427
479
  inputs:
428
480
  file:
429
481
  button_label: 'File'
@@ -473,6 +525,12 @@ en:
473
525
  create: 'Invite %{model}'
474
526
 
475
527
  devise:
528
+ invitations:
529
+ edit:
530
+ title: Set your password
531
+ failure:
532
+ user:
533
+ blocked: User account is no longer active.
476
534
  passwords:
477
535
  edit:
478
536
  title: Change your password
@@ -1,3 +1,5 @@
1
1
  Integral::Engine.routes.draw do
2
2
  Integral::Router.load
3
3
  end
4
+
5
+ ActiveSupport::Notifications.instrument 'integral.routes_loaded'
@@ -0,0 +1,25 @@
1
+ class CreateIntegralNotifications < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :integral_users, :notify_me, :boolean, default: true
4
+
5
+ create_table :integral_notifications do |t|
6
+ t.integer :recipient_id
7
+ t.integer :actor_id
8
+ t.datetime :read_at
9
+ t.string :action
10
+ # (automated index name is too long)
11
+ t.references :subscribable, polymorphic: true, index: { name: 'index_integral_notifications_on_subscribable_type_id' }
12
+
13
+ t.timestamps null: false
14
+ end
15
+
16
+ create_table :integral_notification_subscriptions do |t|
17
+ t.integer :user_id
18
+ t.string :state
19
+ # (automated index name is too long)
20
+ t.references :subscribable, polymorphic: true, index: { name: 'index_integral_subscriptions_on_subscribable_type_id' }
21
+
22
+ t.timestamps null: false
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,5 @@
1
+ class AddStatusToIntegralUsers < ActiveRecord::Migration[5.2]
2
+ def change
3
+ add_column :integral_users, :status, :integer, default: 0
4
+ end
5
+ end
@@ -18,7 +18,9 @@ Integral::Role.create!(name: 'PostManager')
18
18
  Integral::Role.create!(name: 'ListManager')
19
19
 
20
20
  # Demo User
21
- user = Integral::User.create!({ name: 'Integrico', email: 'user@integralrails.com', password: 'password', role_ids: Integral::Role.ids, admin: true })
21
+ user = Integral::User.create!({ name: 'Integrico', email: 'user@integralrails.com', password: 'password', role_ids: Integral::Role.ids, admin: true, status: 1 })
22
+
23
+ return if Rails.env.test? # Test DB should start with blank slate ... although roles and initial user are required
22
24
 
23
25
  # Demo Page
24
26
  host = URI.parse(Rails.application.routes.default_url_options[:host] || 'http://localhost:3000')
@@ -33,16 +35,18 @@ Integral::Page.create!(title: 'Integral CMS - Demo Page',
33
35
  body: renderer.render('integral/pages/_demo', layout: false),
34
36
  status: 1)
35
37
 
36
- # Demo Post
37
- category = Integral::Category.create!(title: 'Uncategorised', description: "Posts which we haven't yet categorized but are sure to grab your attention", slug: 'uncategorized')
38
- Integral::Post.create!(title: 'Integral CMS - Demo Post',
39
- description:'Integral CMS demo post. Integral is a rails content management system (CMS) which gives developers the ability to create a modern website with all the bells and whistles without the hassle.',
40
- body: File.read(File.join(Integral::Engine.root.join('public', 'integral', 'ckeditor_demo_content.html'))),
41
- slug: 'integral-demo',
42
- user: user,
43
- tag_list: 'integral-cms,example-tag',
44
- status: 1,
45
- category: category)
38
+ if Integral.blog_enabled?
39
+ # Demo Post
40
+ category = Integral::Category.create!(title: 'Uncategorised', description: "Posts which we haven't yet categorized but are sure to grab your attention", slug: 'uncategorized')
41
+ Integral::Post.create!(title: 'Integral CMS - Demo Post',
42
+ description:'Integral CMS demo post. Integral is a rails content management system (CMS) which gives developers the ability to create a modern website with all the bells and whistles without the hassle.',
43
+ body: File.read(File.join(Integral::Engine.root.join('public', 'integral', 'ckeditor_demo_content.html'))),
44
+ slug: 'integral-demo',
45
+ user: user,
46
+ tag_list: 'integral-cms,example-tag',
47
+ status: 1,
48
+ category: category)
49
+ end
46
50
 
47
51
  # Main Menu
48
52
  Integral::List.create!({ title: 'Main Menu', list_items: [
@@ -15,6 +15,7 @@ require 'integral/grids/lists_grid'
15
15
  require 'integral/grids/posts_grid'
16
16
  require 'integral/grids/images_grid'
17
17
  require 'integral/acts_as_listable'
18
+ require 'integral/acts_as_integral'
18
19
  require 'integral/widgets/recent_posts'
19
20
  require 'integral/widgets/swiper_list'
20
21
  require 'integral/content_renderer'
@@ -0,0 +1,115 @@
1
+ module Integral
2
+ # Handles adding Integral behaviour to a class
3
+ module ActsAsIntegral
4
+ DEFAULT_OPTIONS ={ notifications: { enabled: true },
5
+ cards: { at_a_glance: true, },
6
+ backend_main_menu: { enabled: true, order: 11 },
7
+ backend_create_menu: { enabled: true, order: 1 }}.freeze
8
+ class << self
9
+ attr_writer :backend_main_menu_items
10
+ attr_writer :backend_create_menu_items
11
+ attr_writer :backend_at_a_glance_card_items
12
+ end
13
+
14
+ # Accessor for backend main menu items
15
+ def self.backend_main_menu_items
16
+ @backend_main_menu_items ||= []
17
+ end
18
+
19
+ # Accessor for backend create menu items
20
+ def self.backend_create_menu_items
21
+ @backend_create_menu_items ||= []
22
+ end
23
+
24
+ # Accessor for at a glance card items
25
+ def self.backend_at_a_glance_card_items
26
+ @backend_at_a_glance_card_items ||= []
27
+ end
28
+
29
+ # Adds item to main backend dashboard at a glance chart
30
+ # @param [Class] item
31
+ def self.add_backend_at_a_glance_card_item(item)
32
+ backend_at_a_glance_card_items << item unless duplicate_menu_item?(item, backend_at_a_glance_card_items)
33
+ end
34
+
35
+ # Adds item to create menu, expects Hash or Class
36
+ def self.add_backend_create_menu_item(item)
37
+ backend_create_menu_items << item unless duplicate_menu_item?(item, backend_create_menu_items)
38
+ end
39
+
40
+ # Adds item to main menu, expects Hash or Class
41
+ def self.add_backend_main_menu_item(item)
42
+ backend_main_menu_items << item unless duplicate_menu_item?(item, backend_main_menu_items)
43
+ end
44
+
45
+ # Checks for existing duplicates in menu. Useful in development when app reloads
46
+ def self.duplicate_menu_item?(item, menu)
47
+ duplicate_found = if item.class == Class
48
+ menu.map(&:to_s).include?(item.to_s)
49
+ else
50
+ menu.select { |item| item.is_a?(Hash) }.map {|item| item[:id] }.include?(item[:id])
51
+ end
52
+
53
+ if duplicate_found
54
+ Rails.logger.error("ActsAsIntegral: Item '#{item.to_s}' not added to menu as it already exists.")
55
+ true
56
+ else
57
+ false
58
+ end
59
+ end
60
+
61
+ ActiveSupport.on_load(:active_record) do
62
+ # ActiveRecord::Base extension
63
+ class ActiveRecord::Base
64
+ # Adds integral behaviour to models
65
+ def self.acts_as_integral(options = {})
66
+ class << self
67
+ attr_accessor :integral_options
68
+
69
+ # @return [Hash] hash representing the class, used to render within the main menu
70
+ def integral_backend_main_menu_item
71
+ {
72
+ icon: integral_icon,
73
+ order: integral_options.dig(:backend_main_menu, :order),
74
+ label: model_name.human.pluralize,
75
+ url: url_helpers.send("backend_#{model_name.route_key}_url"),
76
+ # authorize: proc { policy(self).index? }, can't use this as self is in wrong context
77
+ authorize_class: self,
78
+ authorize_action: :index,
79
+ list_items: [
80
+ { label: I18n.t('integral.navigation.dashboard'), url: url_helpers.send("backend_#{model_name.route_key}_url"), authorize_class: self, authorize_action: :index },
81
+ { label: I18n.t('integral.actions.create'), url: url_helpers.send("new_backend_#{model_name.singular_route_key}_url"), authorize_class: self, authorize_action: :new },
82
+ { label: I18n.t('integral.navigation.listing'), url: url_helpers.send("list_backend_#{model_name.route_key}_url"), authorize_class: self, authorize_action: :list },
83
+ ]
84
+ }
85
+ end
86
+
87
+ # @return [Hash] hash representing the class, used to render within the create menu
88
+ def integral_backend_create_menu_item
89
+ {
90
+ icon: integral_icon,
91
+ order: integral_options.dig(:backend_create_menu, :order),
92
+ label: model_name.human,
93
+ url: url_helpers.send("new_backend_#{model_name.singular_route_key}_url"),
94
+ # authorize: proc { policy(self).index? }, can't use this as self is in wrong context
95
+ authorize_class: self,
96
+ authorize_action: :new,
97
+ }
98
+ end
99
+
100
+ def url_helpers
101
+ Integral::Engine.routes.url_helpers
102
+ end
103
+ end
104
+
105
+ self.integral_options = Integral::ActsAsIntegral::DEFAULT_OPTIONS.deep_merge(options)
106
+ Integral::ActsAsIntegral.add_backend_create_menu_item(self) if integral_options.dig(:backend_create_menu, :enabled)
107
+ Integral::ActsAsIntegral.add_backend_main_menu_item(self) if integral_options.dig(:backend_main_menu, :enabled)
108
+ Integral::ActsAsIntegral.add_backend_at_a_glance_card_item(self) if integral_options.dig(:cards, :at_a_glance)
109
+
110
+ include Integral::Notification::Subscribable if integral_options.dig(:notifications, :enabled)
111
+ end
112
+ end
113
+ end
114
+ end
115
+ end
@@ -16,7 +16,7 @@ module Integral
16
16
  class ActiveRecord::Base
17
17
  # Adds listable behaviour to objects
18
18
  def self.acts_as_listable(_options = {})
19
- Integral::ActsAsListable.objects << self
19
+ Integral::ActsAsListable.objects << self unless Integral::ActsAsListable.objects.map(&:name).include?(self.name)
20
20
 
21
21
  # @return [Hash] instance as a list item
22
22
  # Keys include: id, title, subtitle, image, description, url