diff --git a/.rubocop.yml b/.rubocop.yml index e9964b1c0..34d5b0bda 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -11,6 +11,7 @@ Metrics/ClassLength: Max: 125 Exclude: - 'app/models/conversation.rb' + - 'app/models/contact.rb' - 'app/mailers/conversation_reply_mailer.rb' - 'app/models/message.rb' - 'app/builders/messages/facebook/message_builder.rb' diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 79a2f8d21..6f64656dd 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -50,7 +50,7 @@ decisions when appropriate. ## Scope -This Code of Conduct applies within all community spaces, and also applies when +This Code of Conduct applies within all community spaces and also applies when an individual is officially representing the community in public spaces. Examples of representing our community include using an official e-mail address, posting via an official social media account, or acting as an appointed diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index bfa510323..935bb293e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -2,4 +2,4 @@ Thanks for taking the time to contribute! :tada::+1: -Please refer to our [Contributing Guide](https://www.chatwoot.com/docs/contributing-guide) for detailed instructions. +Please refer to our [Contributing Guide](https://www.chatwoot.com/docs/contributing-guide) for detailed instructions on how to contribute. diff --git a/Gemfile.lock b/Gemfile.lock index b61ef372c..079dd0ee6 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -255,9 +255,9 @@ GEM google-cloud-core (~> 1.6) googleauth (>= 0.16.2, < 2.a) mini_mime (~> 1.0) - google-protobuf (3.18.1) - google-protobuf (3.18.1-universal-darwin) - google-protobuf (3.18.1-x86_64-linux) + google-protobuf (3.19.2) + google-protobuf (3.19.2-x86_64-darwin) + google-protobuf (3.19.2-x86_64-linux) googleapis-common-protos (1.3.12) google-protobuf (~> 3.14) googleapis-common-protos-types (~> 1.2) diff --git a/README.md b/README.md index b2e4ccf4f..c3ad01a5e 100644 --- a/README.md +++ b/README.md @@ -118,4 +118,4 @@ Thanks goes to all these [wonderful people](https://www.chatwoot.com/docs/contri -*Chatwoot* © 2017-2021, Chatwoot Inc - Released under the MIT License. +*Chatwoot* © 2017-2022, Chatwoot Inc - Released under the MIT License. diff --git a/app/builders/messages/message_builder.rb b/app/builders/messages/message_builder.rb index a30394edc..f84e38ca7 100644 --- a/app/builders/messages/message_builder.rb +++ b/app/builders/messages/message_builder.rb @@ -8,9 +8,11 @@ class Messages::MessageBuilder @conversation = conversation @user = user @message_type = params[:message_type] || 'outgoing' - @items = params.to_unsafe_h&.dig(:content_attributes, :items) @attachments = params[:attachments] + return unless params.instance_of?(ActionController::Parameters) + @in_reply_to = params.to_unsafe_h&.dig(:content_attributes, :in_reply_to) + @items = params.to_unsafe_h&.dig(:content_attributes, :items) end def perform diff --git a/app/controllers/api/v1/accounts/automation_rules_controller.rb b/app/controllers/api/v1/accounts/automation_rules_controller.rb new file mode 100644 index 000000000..0ec35c765 --- /dev/null +++ b/app/controllers/api/v1/accounts/automation_rules_controller.rb @@ -0,0 +1,21 @@ +class Api::V1::Accounts::AutomationRulesController < Api::V1::Accounts::BaseController + before_action :check_authorization + + def index + @automation_rules = Current.account.automation_rules + end + + def create + @automation_rule = Current.account.automation_rules.create(automation_rules_permit) + end + + private + + def automation_rules_permit + params.permit( + :name, :description, :event_name, :account_id, + conditions: [:attribute_key, :filter_operator, :query_operator, { values: [] }], + actions: [:action_name, { action_params: [:intiated_at] }] + ) + end +end diff --git a/app/controllers/api/v1/accounts/inboxes_controller.rb b/app/controllers/api/v1/accounts/inboxes_controller.rb index 1720ee692..9f2cfba8c 100644 --- a/app/controllers/api/v1/accounts/inboxes_controller.rb +++ b/app/controllers/api/v1/accounts/inboxes_controller.rb @@ -118,7 +118,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController def permitted_params(channel_attributes = []) params.permit( :name, :avatar, :greeting_enabled, :greeting_message, :enable_email_collect, :csat_survey_enabled, - :enable_auto_assignment, :working_hours_enabled, :out_of_office_message, :timezone, + :enable_auto_assignment, :working_hours_enabled, :out_of_office_message, :timezone, :allow_messages_after_resolved, channel: [:type, *channel_attributes] ) end diff --git a/app/controllers/api/v1/accounts_controller.rb b/app/controllers/api/v1/accounts_controller.rb index a98da15da..f1755b921 100644 --- a/app/controllers/api/v1/accounts_controller.rb +++ b/app/controllers/api/v1/accounts_controller.rb @@ -55,7 +55,7 @@ class Api::V1::AccountsController < Api::BaseController end def check_signup_enabled - raise ActionController::RoutingError, 'Not Found' if GlobalConfig.get_value('ENABLE_ACCOUNT_SIGNUP') == 'false' + raise ActionController::RoutingError, 'Not Found' if GlobalConfigService.load('ENABLE_ACCOUNT_SIGNUP', 'false') == 'false' end def pundit_user diff --git a/app/controllers/api/v1/notification_subscriptions_controller.rb b/app/controllers/api/v1/notification_subscriptions_controller.rb index 98ff9ea7a..5f1cf30e4 100644 --- a/app/controllers/api/v1/notification_subscriptions_controller.rb +++ b/app/controllers/api/v1/notification_subscriptions_controller.rb @@ -7,6 +7,12 @@ class Api::V1::NotificationSubscriptionsController < Api::BaseController render json: notification_subscription end + def destroy + notification_subscription = NotificationSubscription.where(["subscription_attributes->>'push_token' = ?", params[:push_token]]).first + notification_subscription.destroy + head :ok + end + private def set_user diff --git a/app/controllers/super_admin/app_configs_controller.rb b/app/controllers/super_admin/app_configs_controller.rb new file mode 100644 index 000000000..2899a7def --- /dev/null +++ b/app/controllers/super_admin/app_configs_controller.rb @@ -0,0 +1,21 @@ +class SuperAdmin::AppConfigsController < SuperAdmin::ApplicationController + def show + @allowed_configs = %w[FB_APP_ID FB_VERIFY_TOKEN FB_APP_SECRET] + # ref: https://github.com/rubocop/rubocop/issues/7767 + # rubocop:disable Style/HashTransformValues + @fb_config = InstallationConfig.where(name: @allowed_configs) + .pluck(:name, :serialized_value) + .map { |name, serialized_value| [name, serialized_value['value']] } + .to_h + # rubocop:enable Style/HashTransformValues + end + + def create + params['app_config'].each do |key, value| + i = InstallationConfig.where(name: key).first_or_create(value: value, locked: false) + i.value = value + i.save! + end + redirect_to super_admin_app_config_url + end +end diff --git a/app/dashboards/agent_bot_dashboard.rb b/app/dashboards/agent_bot_dashboard.rb index 87605135d..e97a64de8 100644 --- a/app/dashboards/agent_bot_dashboard.rb +++ b/app/dashboards/agent_bot_dashboard.rb @@ -12,6 +12,7 @@ class AgentBotDashboard < Administrate::BaseDashboard avatar_url: AvatarField, id: Field::Number, name: Field::String, + account: Field::BelongsTo.with_options(searchable: true, searchable_field: 'name', order: 'id DESC'), description: Field::String, outgoing_url: Field::String, created_at: Field::DateTime, @@ -26,6 +27,7 @@ class AgentBotDashboard < Administrate::BaseDashboard COLLECTION_ATTRIBUTES = %i[ id avatar_url + account name outgoing_url ].freeze @@ -34,7 +36,7 @@ class AgentBotDashboard < Administrate::BaseDashboard # an array of attributes that will be displayed on the model's show page. SHOW_PAGE_ATTRIBUTES = %i[ id - avatar_url + account name description outgoing_url @@ -45,6 +47,7 @@ class AgentBotDashboard < Administrate::BaseDashboard # on the model's form (`new` and `edit`) pages. FORM_ATTRIBUTES = %i[ name + account description outgoing_url ].freeze diff --git a/app/dispatchers/async_dispatcher.rb b/app/dispatchers/async_dispatcher.rb index 232dcff77..3f95405f9 100644 --- a/app/dispatchers/async_dispatcher.rb +++ b/app/dispatchers/async_dispatcher.rb @@ -16,7 +16,8 @@ class AsyncDispatcher < BaseDispatcher HookListener.instance, InstallationWebhookListener.instance, NotificationListener.instance, - WebhookListener.instance + WebhookListener.instance, + AutomationRuleListener.instance ] end end diff --git a/app/fields/avatar_field.rb b/app/fields/avatar_field.rb index 50633ccd2..a9674eb94 100644 --- a/app/fields/avatar_field.rb +++ b/app/fields/avatar_field.rb @@ -2,6 +2,6 @@ require 'administrate/field/base' class AvatarField < Administrate::Field::Base def avatar_url - data.presence || '/admin/avatar.png' + data.presence&.gsub('?d=404', '?d=mp') end end diff --git a/app/javascript/dashboard/components/buttons/ResolveAction.vue b/app/javascript/dashboard/components/buttons/ResolveAction.vue index b436bf27b..810172ae5 100644 --- a/app/javascript/dashboard/components/buttons/ResolveAction.vue +++ b/app/javascript/dashboard/components/buttons/ResolveAction.vue @@ -52,12 +52,16 @@ {{ this.$t('CONVERSATION.RESOLVE_DROPDOWN.MARK_PENDING') }} + {{ this.$t('CONVERSATION.RESOLVE_DROPDOWN.SNOOZE.NEXT_REPLY') }} @@ -73,6 +80,9 @@ diff --git a/app/javascript/dashboard/components/layout/Sidebar.vue b/app/javascript/dashboard/components/layout/Sidebar.vue index 06ec9201c..cd0d0a08e 100644 --- a/app/javascript/dashboard/components/layout/Sidebar.vue +++ b/app/javascript/dashboard/components/layout/Sidebar.vue @@ -1,7 +1,7 @@ diff --git a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue index 037cf1e73..d2cf8974a 100644 --- a/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue +++ b/app/javascript/dashboard/routes/dashboard/conversation/contact/ContactInfo.vue @@ -10,17 +10,24 @@ />
-

+
+

+ {{ contact.name }} +

- {{ contact.name }} - + -

+

{{ additionalAttributes.description }}

@@ -294,19 +301,20 @@ export default { text-align: left; } +.contact--name-wrap { + display: flex; + align-items: center; + margin-bottom: var(--space-small); +} + .contact--name { text-transform: capitalize; white-space: normal; + margin: 0 var(--space-smaller) 0 0; a { color: var(--color-body); } - - .open-link--icon { - color: var(--color-body); - font-size: var(--font-size-small); - margin-left: var(--space-smaller); - } } .contact--metadata { diff --git a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue index cb61c2f97..79acdfef2 100644 --- a/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue +++ b/app/javascript/dashboard/routes/dashboard/settings/inbox/Settings.vue @@ -215,6 +215,25 @@

+ + @@ -420,6 +439,7 @@ export default { emailCollectEnabled: false, isAgentListUpdating: false, csatSurveyEnabled: false, + allowMessagesAfterResolved: true, selectedInboxName: '', channelWebsiteUrl: '', webhookUrl: '', @@ -583,6 +603,7 @@ export default { this.autoAssignment = this.inbox.enable_auto_assignment; this.emailCollectEnabled = this.inbox.enable_email_collect; this.csatSurveyEnabled = this.inbox.csat_survey_enabled; + this.allowMessagesAfterResolved = this.inbox.allow_messages_after_resolved; this.channelWebsiteUrl = this.inbox.website_url; this.channelWelcomeTitle = this.inbox.welcome_title; this.channelWelcomeTagline = this.inbox.welcome_tagline; @@ -625,6 +646,7 @@ export default { enable_auto_assignment: this.autoAssignment, enable_email_collect: this.emailCollectEnabled, csat_survey_enabled: this.csatSurveyEnabled, + allow_messages_after_resolved: this.allowMessagesAfterResolved, greeting_enabled: this.greetingEnabled, greeting_message: this.greetingMessage || '', channel: { diff --git a/app/javascript/shared/components/FluentIcon/DashboardIcon.vue b/app/javascript/shared/components/FluentIcon/DashboardIcon.vue index c07b9a180..b695aaaa7 100644 --- a/app/javascript/shared/components/FluentIcon/DashboardIcon.vue +++ b/app/javascript/shared/components/FluentIcon/DashboardIcon.vue @@ -1,19 +1,15 @@ diff --git a/app/javascript/shared/components/FluentIcon/Index.vue b/app/javascript/shared/components/FluentIcon/Index.vue index 6b108ff52..7bc98a7a7 100644 --- a/app/javascript/shared/components/FluentIcon/Index.vue +++ b/app/javascript/shared/components/FluentIcon/Index.vue @@ -1,27 +1,23 @@