motor-admin 0.3.16 → 0.4.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.
- checksums.yaml +4 -4
- data/app/channels/motor/application_cable/channel.rb +14 -0
- data/app/channels/motor/application_cable/connection.rb +27 -0
- data/app/channels/motor/notes_channel.rb +9 -0
- data/app/channels/motor/notifications_channel.rb +9 -0
- data/app/controllers/concerns/motor/current_user_method.rb +2 -0
- data/app/controllers/motor/note_tags_controller.rb +13 -0
- data/app/controllers/motor/notes_controller.rb +58 -0
- data/app/controllers/motor/notifications_controller.rb +33 -0
- data/app/controllers/motor/reminders_controller.rb +38 -0
- data/app/controllers/motor/run_queries_controller.rb +1 -1
- data/app/controllers/motor/send_alerts_controller.rb +2 -2
- data/app/controllers/motor/sessions_controller.rb +3 -0
- data/app/controllers/motor/slack_conversations_controller.rb +11 -0
- data/app/controllers/motor/tags_controller.rb +1 -1
- data/app/controllers/motor/ui_controller.rb +9 -1
- data/app/controllers/motor/users_for_autocomplete_controller.rb +23 -0
- data/app/jobs/motor/alert_sending_job.rb +1 -1
- data/app/jobs/motor/notify_note_mentions_job.rb +9 -0
- data/app/jobs/motor/notify_reminder_job.rb +9 -0
- data/app/mailers/motor/alerts_mailer.rb +6 -29
- data/app/mailers/motor/application_mailer.rb +27 -1
- data/app/mailers/motor/notifications_mailer.rb +33 -0
- data/app/models/motor/note.rb +18 -0
- data/app/models/motor/note_tag.rb +7 -0
- data/app/models/motor/note_tag_tag.rb +8 -0
- data/app/models/motor/notification.rb +14 -0
- data/app/models/motor/reminder.rb +13 -0
- data/app/views/layouts/motor/application.html.erb +4 -1
- data/app/views/layouts/motor/mailer.html.erb +72 -0
- data/app/views/motor/alerts_mailer/alert_email.html.erb +52 -124
- data/app/views/motor/notifications_mailer/notify_mention_email.html.erb +28 -0
- data/app/views/motor/notifications_mailer/notify_reminder_email.html.erb +28 -0
- data/config/locales/el.yml +25 -0
- data/config/locales/en.yml +33 -2
- data/config/locales/es.yml +33 -2
- data/config/locales/pt.yml +33 -2
- data/config/routes.rb +9 -0
- data/lib/generators/motor/install_notes_generator.rb +22 -0
- data/lib/generators/motor/templates/install.rb +77 -0
- data/lib/generators/motor/templates/install_notes.rb +83 -0
- data/lib/generators/motor/upgrade_generator.rb +13 -6
- data/lib/motor/admin.rb +13 -1
- data/lib/motor/alerts/slack_sender.rb +74 -0
- data/lib/motor/alerts.rb +42 -0
- data/lib/motor/api_query/apply_scope.rb +1 -0
- data/lib/motor/build_schema/apply_permissions.rb +8 -0
- data/lib/motor/build_schema/defaults.rb +15 -1
- data/lib/motor/build_schema/load_from_rails.rb +4 -1
- data/lib/motor/build_schema.rb +7 -0
- data/lib/motor/configs/build_ui_app_tag.rb +73 -8
- data/lib/motor/configs.rb +3 -4
- data/lib/motor/notes/notify_mentions.rb +73 -0
- data/lib/motor/notes/notify_reminder.rb +49 -0
- data/lib/motor/notes/persist.rb +36 -0
- data/lib/motor/notes/reminders_scheduler.rb +39 -0
- data/lib/motor/notes/tags.rb +34 -0
- data/lib/motor/notes.rb +12 -0
- data/lib/motor/queries/run_query.rb +66 -3
- data/lib/motor/resources/fetch_configured_model.rb +19 -3
- data/lib/motor/resources.rb +1 -1
- data/lib/motor/slack/client.rb +62 -0
- data/lib/motor/slack.rb +16 -0
- data/lib/motor/version.rb +1 -1
- data/lib/motor.rb +19 -0
- data/ui/dist/{main-726aa7f6805676af4d21.css.gz → main-99bab2664944ee03d10f.css.gz} +0 -0
- data/ui/dist/main-99bab2664944ee03d10f.js.gz +0 -0
- data/ui/dist/manifest.json +5 -5
- metadata +36 -4
- data/ui/dist/main-726aa7f6805676af4d21.js.gz +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ca54cd5d999594ce72fba85a996c63bb99ef87cb58dddd02f91af2c97e8d9508
|
4
|
+
data.tar.gz: 26c2f9525f0324a30a1ae241a466e85cce271434a2b2d29f144387f01486d712
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97ade114c8bedf11dcf2586ec76fd629efd7517b4319e32fe3ce23a3c6e38f8e179e233e0fc202147785b9b0e9d3757af0064eadfb0eb84a177ce87f63b646ce
|
7
|
+
data.tar.gz: ccd281c10b4950741da0ecca8d3a9e1296186a8d96fb7db04b41e16809064dbed7334f0c0250f56779ae1fed660da9572c8abc0aa8dfc137684b551126176ce0
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class DummyChannel
|
5
|
+
def self.broadcast_to(*_args)
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
module ApplicationCable
|
11
|
+
class Channel < defined?(ActionCable) ? ActionCable::Channel::Base : Motor::DummyChannel
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
module ApplicationCable
|
5
|
+
class Connection < defined?(ActionCable) ? ActionCable::Connection::Base : Object
|
6
|
+
identified_by :current_user if defined?(ActionCable)
|
7
|
+
|
8
|
+
def connect
|
9
|
+
self.current_user = find_verified_user
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def find_verified_user
|
15
|
+
return unless env['warden']
|
16
|
+
|
17
|
+
if env['warden'].respond_to?(:admin_user)
|
18
|
+
env['warden'].admin_user
|
19
|
+
elsif env['warden'].respond_to?(:admin)
|
20
|
+
env['warden'].admin
|
21
|
+
else
|
22
|
+
env['warden'].user
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NoteTagsController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :note_tag
|
8
|
+
|
9
|
+
def index
|
10
|
+
render json: { data: @note_tags.to_a }
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NotesController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :note, only: %i[index show update destroy]
|
8
|
+
before_action :build_note, only: :create
|
9
|
+
authorize_resource :note, only: :create
|
10
|
+
|
11
|
+
def index
|
12
|
+
@notes = Motor::ApiQuery.call(@notes.active.order(created_at: :asc), params)
|
13
|
+
|
14
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@notes, params, current_ability) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def show
|
18
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def create
|
22
|
+
Motor::Notes::Persist.call(@note, current_user)
|
23
|
+
Motor::NotifyNoteMentionsJob.perform_later(@note, current_user)
|
24
|
+
|
25
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
|
26
|
+
end
|
27
|
+
|
28
|
+
def update
|
29
|
+
@note.assign_attributes(note_params)
|
30
|
+
|
31
|
+
Motor::Notes::Persist.call(@note, current_user)
|
32
|
+
Motor::NotifyNoteMentionsJob.perform_later(@note, current_user)
|
33
|
+
|
34
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@note, params, current_ability) }
|
35
|
+
end
|
36
|
+
|
37
|
+
def destroy
|
38
|
+
@note.update!(deleted_at: Time.current)
|
39
|
+
|
40
|
+
Motor::NotesChannel.broadcast_to(
|
41
|
+
@note.values_at(:record_type, :record_id).join(':'),
|
42
|
+
['remove', @note.as_json(only: %i[id body created_at updated_at])]
|
43
|
+
)
|
44
|
+
|
45
|
+
head :ok
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def build_note
|
51
|
+
@note = Motor::Note.new(note_params.merge(author: current_user))
|
52
|
+
end
|
53
|
+
|
54
|
+
def note_params
|
55
|
+
params.require(:data).permit(:body, :record_id, :record_type)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NotificationsController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :notification
|
8
|
+
|
9
|
+
def index
|
10
|
+
@notifications =
|
11
|
+
Motor::ApiQuery.call(@notifications.where(recipient: current_user).order(created_at: :desc), params)
|
12
|
+
|
13
|
+
render json: {
|
14
|
+
data: Motor::ApiQuery::BuildJson.call(@notifications, params, current_ability),
|
15
|
+
meta: Motor::ApiQuery::BuildMeta.call(@notifications, params)
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def update
|
20
|
+
@notification.update!(notification_params)
|
21
|
+
|
22
|
+
Motor::NotificationsChannel.broadcast_to(current_user, ['update', @notification.as_json(include: %i[record])])
|
23
|
+
|
24
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@notification, params, current_ability) }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
|
29
|
+
def notification_params
|
30
|
+
params.require(:data).permit(:status)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class RemindersController < ApiBaseController
|
5
|
+
wrap_parameters :data, except: %i[include fields]
|
6
|
+
|
7
|
+
load_and_authorize_resource :reminder
|
8
|
+
|
9
|
+
def create
|
10
|
+
@reminder.update!(author: current_user, recipient: current_user)
|
11
|
+
|
12
|
+
broadcast_note_update(@reminder.record)
|
13
|
+
|
14
|
+
render json: { data: Motor::ApiQuery::BuildJson.call(@reminder, params, current_ability) }
|
15
|
+
end
|
16
|
+
|
17
|
+
def destroy
|
18
|
+
@reminder.destroy!
|
19
|
+
|
20
|
+
broadcast_note_update(@reminder.record)
|
21
|
+
|
22
|
+
head :ok
|
23
|
+
end
|
24
|
+
|
25
|
+
private
|
26
|
+
|
27
|
+
def broadcast_note_update(note)
|
28
|
+
Motor::NotesChannel.broadcast_to(
|
29
|
+
note.values_at(:record_type, :record_id).join(':'),
|
30
|
+
['update', note.as_json(include: %i[author tags reminders])]
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
def reminder_params
|
35
|
+
params.require(:data).permit(:record_id, :record_type, :scheduled_at)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -20,7 +20,7 @@ module Motor
|
|
20
20
|
private
|
21
21
|
|
22
22
|
def render_result
|
23
|
-
query_result = Queries::RunQuery.call(@query, variables_hash: variables_params,
|
23
|
+
query_result = Queries::RunQuery.call(@query, variables_hash: variables_params.to_unsafe_h,
|
24
24
|
limit: params[:limit].presence,
|
25
25
|
filters: filter_params)
|
26
26
|
|
@@ -8,7 +8,7 @@ module Motor
|
|
8
8
|
authorize_resource :alert, only: :create
|
9
9
|
|
10
10
|
def create
|
11
|
-
|
11
|
+
Motor::Alerts.send_alert(@alert)
|
12
12
|
|
13
13
|
head :ok
|
14
14
|
end
|
@@ -20,7 +20,7 @@ module Motor
|
|
20
20
|
end
|
21
21
|
|
22
22
|
def alert_params
|
23
|
-
params.require(:data).permit(:query_id, :name, :to_emails)
|
23
|
+
params.require(:data).permit(:query_id, :name, :to_emails, :description, preferences: {})
|
24
24
|
end
|
25
25
|
end
|
26
26
|
end
|
@@ -4,7 +4,7 @@ module Motor
|
|
4
4
|
class UiController < ApplicationController
|
5
5
|
layout 'motor/application'
|
6
6
|
|
7
|
-
helper_method :current_user, :current_ability, :cache_keys
|
7
|
+
helper_method :current_user, :current_ability, :cache_keys, :custom_html
|
8
8
|
|
9
9
|
before_action :set_i18n_locale
|
10
10
|
|
@@ -30,6 +30,14 @@ module Motor
|
|
30
30
|
render :show
|
31
31
|
end
|
32
32
|
|
33
|
+
def custom_html
|
34
|
+
Motor::Admin.config.custom_html.presence || begin
|
35
|
+
configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
|
36
|
+
|
37
|
+
configs.find { |c| c.key == 'ui.custom_html' }&.value
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
33
41
|
def set_i18n_locale
|
34
42
|
configs = Motor::Configs::LoadFromCache.load_configs(cache_key: cache_keys[:configs])
|
35
43
|
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class UsersForAutocompleteController < ApiBaseController
|
5
|
+
def index
|
6
|
+
authorize!(:create, Motor::Note)
|
7
|
+
|
8
|
+
render json: { data: user_emails }
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
|
13
|
+
def user_emails
|
14
|
+
user_class = Motor::AdminUser if defined?(Motor::AdminUser)
|
15
|
+
user_class ||= AdminUser if defined?(AdminUser)
|
16
|
+
user_class ||= User if defined?(User)
|
17
|
+
|
18
|
+
return user_class.distinct.limit(100).pluck(:email) if user_class
|
19
|
+
|
20
|
+
[]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -5,7 +5,7 @@ module Motor
|
|
5
5
|
def perform(alert)
|
6
6
|
Motor::AlertLock.create!(alert_id: alert.id, lock_timestamp: alert.cron.previous_time.to_i)
|
7
7
|
|
8
|
-
Motor::
|
8
|
+
Motor::Alerts.send_alert(alert)
|
9
9
|
rescue ActiveRecord::RecordNotUnique
|
10
10
|
nil
|
11
11
|
end
|
@@ -6,9 +6,10 @@ module Motor
|
|
6
6
|
SENDER_NOT_SET_ERROR_MESSAGE =
|
7
7
|
'Please specify your sender address via MOTOR_ALERTS_FROM_ADDRESS environment variable'
|
8
8
|
|
9
|
-
def alert_email(alert)
|
9
|
+
def alert_email(alert, email: nil)
|
10
10
|
@alert = alert
|
11
|
-
@query_result = Queries::RunQuery.call(alert.query
|
11
|
+
@query_result = Queries::RunQuery.call(alert.query,
|
12
|
+
variables_hash: { current_user_email: email })
|
12
13
|
|
13
14
|
return if @alert.preferences[:send_empty].blank? && @query_result.data.blank?
|
14
15
|
|
@@ -18,7 +19,7 @@ module Motor
|
|
18
19
|
|
19
20
|
mail(
|
20
21
|
from: from_address,
|
21
|
-
to: alert.to_emails,
|
22
|
+
to: email || alert.to_emails,
|
22
23
|
subject: alert.name.presence || @alert.query.name
|
23
24
|
)
|
24
25
|
end
|
@@ -29,34 +30,10 @@ module Motor
|
|
29
30
|
attachments["#{alert.name.presence || 'data'}.csv"] = generate_csv(query_result)
|
30
31
|
end
|
31
32
|
|
32
|
-
def generate_csv(
|
33
|
-
rows = [
|
33
|
+
def generate_csv(query_result)
|
34
|
+
rows = [query_result.columns.pluck(:name)] + query_result.data
|
34
35
|
|
35
36
|
rows.map(&:to_csv).join
|
36
37
|
end
|
37
|
-
|
38
|
-
def from_address
|
39
|
-
from = ENV['MOTOR_ALERTS_FROM_ADDRESS'].presence || ENV['MOTOR_EMAIL_ADDRESS'].presence
|
40
|
-
|
41
|
-
from ||= application_mailer_default_from
|
42
|
-
from ||= mailer_config_from_address
|
43
|
-
from ||= "reports@#{ENV['HOST'].delete_prefix('www.')}" if ENV['HOST'].present?
|
44
|
-
|
45
|
-
from ||= 'reports@example.com' if Rails.env.development?
|
46
|
-
|
47
|
-
from
|
48
|
-
end
|
49
|
-
|
50
|
-
def application_mailer_default_from
|
51
|
-
return if !defined?(::ApplicationMailer) || ::ApplicationMailer.default[:from].to_s.include?('example.com')
|
52
|
-
|
53
|
-
::ApplicationMailer.default[:from].presence
|
54
|
-
end
|
55
|
-
|
56
|
-
def mailer_config_from_address
|
57
|
-
return if Rails.application.config.action_mailer.default_url_options&.dig(:host).blank?
|
58
|
-
|
59
|
-
"reports@#{Rails.application.config.action_mailer.default_url_options[:host].delete_prefix('www.')}"
|
60
|
-
end
|
61
38
|
end
|
62
39
|
end
|
@@ -2,6 +2,32 @@
|
|
2
2
|
|
3
3
|
module Motor
|
4
4
|
class ApplicationMailer < ActionMailer::Base
|
5
|
-
layout 'mailer'
|
5
|
+
layout 'motor/mailer'
|
6
|
+
|
7
|
+
private
|
8
|
+
|
9
|
+
def from_address
|
10
|
+
from = ENV['MOTOR_ALERTS_FROM_ADDRESS'].presence || ENV['MOTOR_EMAIL_ADDRESS'].presence
|
11
|
+
|
12
|
+
from ||= application_mailer_default_from
|
13
|
+
from ||= mailer_config_from_address
|
14
|
+
from ||= "reports@#{ENV['HOST'].delete_prefix('www.')}" if ENV['HOST'].present?
|
15
|
+
|
16
|
+
from ||= 'reports@example.com' if Rails.env.development?
|
17
|
+
|
18
|
+
from
|
19
|
+
end
|
20
|
+
|
21
|
+
def application_mailer_default_from
|
22
|
+
return if !defined?(::ApplicationMailer) || ::ApplicationMailer.default[:from].to_s.include?('example.com')
|
23
|
+
|
24
|
+
::ApplicationMailer.default[:from].presence
|
25
|
+
end
|
26
|
+
|
27
|
+
def mailer_config_from_address
|
28
|
+
return if Rails.application.config.action_mailer.default_url_options&.dig(:host).blank?
|
29
|
+
|
30
|
+
"reports@#{Rails.application.config.action_mailer.default_url_options[:host].delete_prefix('www.')}"
|
31
|
+
end
|
6
32
|
end
|
7
33
|
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class NotificationsMailer < ApplicationMailer
|
5
|
+
def notify_mention_email(notification)
|
6
|
+
@notification = notification
|
7
|
+
|
8
|
+
@note = notification.record
|
9
|
+
|
10
|
+
subject =
|
11
|
+
"#{@note.author&.email || 'Anonymous'} mentioned you in " \
|
12
|
+
"#{@note.record.class.model_name.human} ##{@note.record.id}"
|
13
|
+
|
14
|
+
mail(
|
15
|
+
from: from_address,
|
16
|
+
to: notification.recipient.email,
|
17
|
+
subject: subject
|
18
|
+
)
|
19
|
+
end
|
20
|
+
|
21
|
+
def notify_reminder_email(notification)
|
22
|
+
@notification = notification
|
23
|
+
|
24
|
+
@note = notification.record.record
|
25
|
+
|
26
|
+
mail(
|
27
|
+
from: from_address,
|
28
|
+
to: notification.recipient.email,
|
29
|
+
subject: "Reminder for #{@note.record.class.model_name.human} ##{@note.record.id}"
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class Note < ::Motor::ApplicationRecord
|
5
|
+
audited
|
6
|
+
|
7
|
+
belongs_to :author, polymorphic: true, optional: true
|
8
|
+
belongs_to :record, polymorphic: true
|
9
|
+
|
10
|
+
has_many :note_tag_tags, dependent: :destroy
|
11
|
+
has_many :tags, through: :note_tag_tags, class_name: 'Motor::NoteTag'
|
12
|
+
|
13
|
+
has_many :reminders, as: :record, dependent: :destroy
|
14
|
+
has_many :notifications, as: :record, dependent: :destroy
|
15
|
+
|
16
|
+
scope :active, -> { where(deleted_at: nil) }
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class Notification < ::Motor::ApplicationRecord
|
5
|
+
STATUSES = [
|
6
|
+
PENDING = 'pending'
|
7
|
+
].freeze
|
8
|
+
|
9
|
+
belongs_to :record, polymorphic: true, optional: true
|
10
|
+
belongs_to :recipient, polymorphic: true
|
11
|
+
|
12
|
+
attribute :status, type: :string, default: PENDING
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Motor
|
4
|
+
class Reminder < ::Motor::ApplicationRecord
|
5
|
+
audited
|
6
|
+
|
7
|
+
belongs_to :record, polymorphic: true, optional: true
|
8
|
+
belongs_to :author, polymorphic: true
|
9
|
+
belongs_to :recipient, polymorphic: true
|
10
|
+
|
11
|
+
has_many :notifications, as: :record, dependent: :destroy
|
12
|
+
end
|
13
|
+
end
|
@@ -1,7 +1,7 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
2
|
<html lang="<%= I18n.locale %>">
|
3
3
|
<head>
|
4
|
-
<title
|
4
|
+
<title><%= Motor.company_name %></title>
|
5
5
|
<meta name="viewport" content="width=device-width,initial-scale=1.0,maximum-scale=1.0,user-scalable=no">
|
6
6
|
<link rel="stylesheet" media="all" href="<%= Motor::Assets.asset_path('main.css') %>">
|
7
7
|
<%= csrf_meta_tags %>
|
@@ -10,5 +10,8 @@
|
|
10
10
|
<body>
|
11
11
|
<%= yield %>
|
12
12
|
<script src="<%= Motor::Assets.asset_path('main.js') %>"></script>
|
13
|
+
<% if params[:controller] === 'motor/ui' %>
|
14
|
+
<%= raw(custom_html) %>
|
15
|
+
<% end %>
|
13
16
|
</body>
|
14
17
|
</html>
|
@@ -0,0 +1,72 @@
|
|
1
|
+
<!doctype html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<meta name="viewport" content="width=device-width">
|
5
|
+
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
6
|
+
<style>
|
7
|
+
@media only screen and (max-width: 620px) {
|
8
|
+
table[class=body] h1 {
|
9
|
+
font-size: 28px !important;
|
10
|
+
margin-bottom: 10px !important;
|
11
|
+
}
|
12
|
+
table[class=body] p,
|
13
|
+
table[class=body] ul,
|
14
|
+
table[class=body] ol,
|
15
|
+
table[class=body] td,
|
16
|
+
table[class=body] span,
|
17
|
+
table[class=body] a {
|
18
|
+
font-size: 14px !important;
|
19
|
+
}
|
20
|
+
table[class=body] .wrapper,
|
21
|
+
table[class=body] .article {
|
22
|
+
padding: 10px !important;
|
23
|
+
}
|
24
|
+
table[class=body] .content {
|
25
|
+
padding: 0 !important;
|
26
|
+
}
|
27
|
+
table[class=body] .container {
|
28
|
+
padding: 0 !important;
|
29
|
+
width: 100% !important;
|
30
|
+
}
|
31
|
+
table[class=body] .main {
|
32
|
+
border-left-width: 0 !important;
|
33
|
+
border-radius: 0 !important;
|
34
|
+
border-right-width: 0 !important;
|
35
|
+
}
|
36
|
+
}
|
37
|
+
|
38
|
+
@media all {
|
39
|
+
.ExternalClass {
|
40
|
+
width: 100%;
|
41
|
+
}
|
42
|
+
.ExternalClass,
|
43
|
+
.ExternalClass p,
|
44
|
+
.ExternalClass span,
|
45
|
+
.ExternalClass font,
|
46
|
+
.ExternalClass td,
|
47
|
+
.ExternalClass div {
|
48
|
+
line-height: 100%;
|
49
|
+
}
|
50
|
+
.apple-link a {
|
51
|
+
color: inherit !important;
|
52
|
+
font-family: inherit !important;
|
53
|
+
font-size: inherit !important;
|
54
|
+
font-weight: inherit !important;
|
55
|
+
line-height: inherit !important;
|
56
|
+
text-decoration: none !important;
|
57
|
+
}
|
58
|
+
#MessageViewBody a {
|
59
|
+
color: inherit;
|
60
|
+
text-decoration: none;
|
61
|
+
font-size: inherit;
|
62
|
+
font-family: inherit;
|
63
|
+
font-weight: inherit;
|
64
|
+
line-height: inherit;
|
65
|
+
}
|
66
|
+
}
|
67
|
+
</style>
|
68
|
+
</head>
|
69
|
+
<body class="" style="background-color: #f9fbfc; font-family: sans-serif; -webkit-font-smoothing: antialiased; font-size: 14px; line-height: 1.4; margin: 0; padding: 0; -ms-text-size-adjust: 100%; -webkit-text-size-adjust: 100%;">
|
70
|
+
<%= yield %>
|
71
|
+
</body>
|
72
|
+
</html>
|