Compare commits
6
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
78ddea8aab | ||
|
|
011deeeb9c | ||
|
|
7a601b9a23 | ||
|
|
6c2aed2f40 | ||
|
|
d68293f93b | ||
|
|
8ac5af649a |
@@ -31,6 +31,9 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
end
|
end
|
||||||
|
|
||||||
def create
|
def create
|
||||||
|
validate_new_email_channel
|
||||||
|
return if performed?
|
||||||
|
|
||||||
ActiveRecord::Base.transaction do
|
ActiveRecord::Base.transaction do
|
||||||
channel = create_channel
|
channel = create_channel
|
||||||
@inbox = Current.account.inboxes.build(
|
@inbox = Current.account.inboxes.build(
|
||||||
@@ -38,11 +41,12 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
name: inbox_name(channel),
|
name: inbox_name(channel),
|
||||||
channel: channel
|
channel: channel
|
||||||
}.merge(
|
}.merge(
|
||||||
permitted_params.except(:channel)
|
permitted_params.except(:channel, :imap_fetch_interval)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@inbox.save!
|
@inbox.save!
|
||||||
end
|
end
|
||||||
|
enqueue_initial_imap_fetch
|
||||||
end
|
end
|
||||||
|
|
||||||
def update
|
def update
|
||||||
@@ -96,6 +100,26 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
account_channels_method.create!(permitted_params(channel_type_from_params::EDITABLE_ATTRS)[:channel].except(:type))
|
account_channels_method.create!(permitted_params(channel_type_from_params::EDITABLE_ATTRS)[:channel].except(:type))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def validate_new_email_channel
|
||||||
|
return unless params.dig(:channel, :type) == 'email'
|
||||||
|
|
||||||
|
validate_email_channel(Channel::Email::EDITABLE_ATTRS)
|
||||||
|
rescue StandardError => e
|
||||||
|
render json: { message: e }, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def enqueue_initial_imap_fetch
|
||||||
|
return unless @inbox.channel.is_a?(Channel::Email)
|
||||||
|
return unless @inbox.channel.imap_enabled?
|
||||||
|
|
||||||
|
::Inboxes::FetchImapEmailsJob.perform_later(@inbox.channel, initial_imap_fetch_interval)
|
||||||
|
end
|
||||||
|
|
||||||
|
def initial_imap_fetch_interval
|
||||||
|
interval = params[:imap_fetch_interval].to_i
|
||||||
|
[1, 7, 30].include?(interval) ? interval : 1
|
||||||
|
end
|
||||||
|
|
||||||
def allowed_channel_types
|
def allowed_channel_types
|
||||||
%w[web_widget api email line telegram whatsapp sms]
|
%w[web_widget api email line telegram whatsapp sms]
|
||||||
end
|
end
|
||||||
@@ -168,7 +192,7 @@ class Api::V1::Accounts::InboxesController < Api::V1::Accounts::BaseController
|
|||||||
def permitted_params(channel_attributes = [])
|
def permitted_params(channel_attributes = [])
|
||||||
# We will remove this line after fixing https://linear.app/chatwoot/issue/CW-1567/null-value-passed-as-null-string-to-backend
|
# We will remove this line after fixing https://linear.app/chatwoot/issue/CW-1567/null-value-passed-as-null-string-to-backend
|
||||||
params.each { |k, v| params[k] = params[k] == 'null' ? nil : v }
|
params.each { |k, v| params[k] = params[k] == 'null' ? nil : v }
|
||||||
params.permit(*inbox_attributes, channel: [:type, *channel_attributes])
|
params.permit(:imap_fetch_interval, *inbox_attributes, channel: [:type, *channel_attributes])
|
||||||
end
|
end
|
||||||
|
|
||||||
def channel_type_from_params
|
def channel_type_from_params
|
||||||
|
|||||||
@@ -77,6 +77,7 @@ class DashboardController < ActionController::Base
|
|||||||
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v18.0'),
|
FACEBOOK_API_VERSION: GlobalConfigService.load('FACEBOOK_API_VERSION', 'v18.0'),
|
||||||
WHATSAPP_APP_ID: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
|
WHATSAPP_APP_ID: GlobalConfigService.load('WHATSAPP_APP_ID', ''),
|
||||||
WHATSAPP_CONFIGURATION_ID: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', ''),
|
WHATSAPP_CONFIGURATION_ID: GlobalConfigService.load('WHATSAPP_CONFIGURATION_ID', ''),
|
||||||
|
INBOUND_EMAIL_DOMAIN_PRESENT: GlobalConfigService.load('MAILER_INBOUND_EMAIL_DOMAIN', '').present?,
|
||||||
IS_ENTERPRISE: ChatwootApp.enterprise?,
|
IS_ENTERPRISE: ChatwootApp.enterprise?,
|
||||||
AZURE_APP_ID: GlobalConfigService.load('AZURE_APP_ID', ''),
|
AZURE_APP_ID: GlobalConfigService.load('AZURE_APP_ID', ''),
|
||||||
GIT_SHA: GIT_HASH,
|
GIT_SHA: GIT_HASH,
|
||||||
|
|||||||
@@ -401,11 +401,25 @@
|
|||||||
"API": {
|
"API": {
|
||||||
"ERROR_MESSAGE": "We were not able to save the email channel"
|
"ERROR_MESSAGE": "We were not able to save the email channel"
|
||||||
},
|
},
|
||||||
"FINISH_MESSAGE": "Your email inbox has been created successfully! You can start forwarding your emails to the address below, or configure SMTP and IMAP credentials to send and receive emails directly.",
|
"SETUP_OPTIONS": {
|
||||||
"FINISH_MESSAGE_NO_FORWARDING": "Your email inbox has been created successfully! You need to configure SMTP and IMAP credentials to send and receive emails. Without these settings, no emails will be processed.",
|
"FORWARDING": {
|
||||||
|
"TITLE": "Email forwarding",
|
||||||
|
"DESCRIPTION": "Use a forwarding address"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"FETCH_EMAILS_FROM": "Fetch emails from",
|
||||||
|
"IMPORT_OPTIONS": {
|
||||||
|
"TITLE": "Initial import",
|
||||||
|
"ONE_DAY": "Last 1 day",
|
||||||
|
"SEVEN_DAYS": "Last 7 days",
|
||||||
|
"THIRTY_DAYS": "Last 30 days"
|
||||||
|
},
|
||||||
|
"FINISH_MESSAGE_FORWARDING": "Your email inbox is ready. Set up a forwarding rule in your email provider to start receiving conversations in Chatwoot.",
|
||||||
|
"FINISH_MESSAGE_IMAP_SMTP": "Your email inbox is connected. Chatwoot will fetch new emails from your mailbox and use the SMTP settings for replies.",
|
||||||
"FORWARDING_ADDRESS_LABEL": "Forward emails to this address:",
|
"FORWARDING_ADDRESS_LABEL": "Forward emails to this address:",
|
||||||
"CONFIGURE_SMTP_IMAP_LINK": "Click here",
|
"FORWARDING_RULE_HELP": "Use this address as the destination when you create the forwarding rule in your mailbox.",
|
||||||
"CONFIGURE_SMTP_IMAP_TEXT": " to configure IMAP and SMTP settings"
|
"CONFIGURE_EMAIL_SETTINGS_LINK": "Open email settings",
|
||||||
|
"MANAGE_SMTP_IMAP_TEXT": " to review or update IMAP and SMTP settings."
|
||||||
},
|
},
|
||||||
"LINE_CHANNEL": {
|
"LINE_CHANNEL": {
|
||||||
"TITLE": "LINE Channel",
|
"TITLE": "LINE Channel",
|
||||||
@@ -520,8 +534,8 @@
|
|||||||
"DESC": "You have successfully finished integrating your Facebook Page with Chatwoot. Next time a customer messages your Page, the conversation will automatically appear on your inbox.<br>We are also providing you with a widget script that you can easily add to your website. Once this is live on your website, customers can message you right from your website without the help of any external tool and the conversation will appear right here, on Chatwoot.<br>Cool, huh? Well, we sure try to be :)"
|
"DESC": "You have successfully finished integrating your Facebook Page with Chatwoot. Next time a customer messages your Page, the conversation will automatically appear on your inbox.<br>We are also providing you with a widget script that you can easily add to your website. Once this is live on your website, customers can message you right from your website without the help of any external tool and the conversation will appear right here, on Chatwoot.<br>Cool, huh? Well, we sure try to be :)"
|
||||||
},
|
},
|
||||||
"EMAIL_PROVIDER": {
|
"EMAIL_PROVIDER": {
|
||||||
"TITLE": "Select your email provider",
|
"TITLE": "Set up your email inbox",
|
||||||
"DESCRIPTION": "Select an email provider from the list below. If you don't see your email provider in the list, you can select the other provider option and provide the IMAP and SMTP Credentials."
|
"DESCRIPTION": "Connect a provider, use IMAP/SMTP, or forward emails to Chatwoot."
|
||||||
},
|
},
|
||||||
"MICROSOFT": {
|
"MICROSOFT": {
|
||||||
"TITLE": "Microsoft Email",
|
"TITLE": "Microsoft Email",
|
||||||
@@ -795,8 +809,8 @@
|
|||||||
"HMAC_MANDATORY_DESCRIPTION": "If enabled, requests that cannot be verified will be rejected.",
|
"HMAC_MANDATORY_DESCRIPTION": "If enabled, requests that cannot be verified will be rejected.",
|
||||||
"INBOX_IDENTIFIER": "Inbox Identifier",
|
"INBOX_IDENTIFIER": "Inbox Identifier",
|
||||||
"INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.",
|
"INBOX_IDENTIFIER_SUB_TEXT": "Use the `inbox_identifier` token shown here to authentication your API clients.",
|
||||||
"FORWARD_EMAIL_TITLE": "Forward to Email",
|
"FORWARD_EMAIL_TITLE": "Email forwarding",
|
||||||
"FORWARD_EMAIL_SUB_TEXT": "Start forwarding your emails to the following email address.",
|
"FORWARD_EMAIL_SUB_TEXT": "Forward emails from your provider to this address.",
|
||||||
"FORWARD_EMAIL_NOT_CONFIGURED": "Forwarding emails to your inbox is currently disabled on this installation. To use this feature, it must be enabled by your administrator. Please get in touch with them to proceed.",
|
"FORWARD_EMAIL_NOT_CONFIGURED": "Forwarding emails to your inbox is currently disabled on this installation. To use this feature, it must be enabled by your administrator. Please get in touch with them to proceed.",
|
||||||
"ALLOW_MESSAGES_AFTER_RESOLVED": "Allow messages after conversation resolved",
|
"ALLOW_MESSAGES_AFTER_RESOLVED": "Allow messages after conversation resolved",
|
||||||
"ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "Allow the end-users to send messages even after the conversation is resolved.",
|
"ALLOW_MESSAGES_AFTER_RESOLVED_SUB_TEXT": "Allow the end-users to send messages even after the conversation is resolved.",
|
||||||
@@ -1017,6 +1031,7 @@
|
|||||||
},
|
},
|
||||||
"IMAP": {
|
"IMAP": {
|
||||||
"TITLE": "IMAP",
|
"TITLE": "IMAP",
|
||||||
|
"CREATE_HELP": "Receive emails from this mailbox.",
|
||||||
"SUBTITLE": "Set your IMAP details",
|
"SUBTITLE": "Set your IMAP details",
|
||||||
"NOTE_TEXT": "To enable SMTP, please configure IMAP.",
|
"NOTE_TEXT": "To enable SMTP, please configure IMAP.",
|
||||||
"UPDATE": "Update IMAP settings",
|
"UPDATE": "Update IMAP settings",
|
||||||
@@ -1050,11 +1065,12 @@
|
|||||||
"SUBTITLE": "Reauthorize your MICROSOFT account"
|
"SUBTITLE": "Reauthorize your MICROSOFT account"
|
||||||
},
|
},
|
||||||
"SMTP": {
|
"SMTP": {
|
||||||
"TITLE": "SMTP",
|
"TITLE": "Outgoing email",
|
||||||
"SUBTITLE": "Set your SMTP details",
|
"CREATE_HELP": "Send replies through this mailbox.",
|
||||||
|
"SUBTITLE": "Send replies using your own email provider.",
|
||||||
"UPDATE": "Update SMTP settings",
|
"UPDATE": "Update SMTP settings",
|
||||||
"TOGGLE_AVAILABILITY": "Enable SMTP configuration for this inbox",
|
"TOGGLE_AVAILABILITY": "Send replies via a custom mail server",
|
||||||
"TOGGLE_HELP": "Enabling SMTP will help the user to send email",
|
"TOGGLE_HELP": "Optional. Configure SMTP when replies should be sent through your own email provider.",
|
||||||
"EDIT": {
|
"EDIT": {
|
||||||
"SUCCESS_MESSAGE": "SMTP settings updated successfully",
|
"SUCCESS_MESSAGE": "SMTP settings updated successfully",
|
||||||
"ERROR_MESSAGE": "Unable to update SMTP settings"
|
"ERROR_MESSAGE": "Unable to update SMTP settings"
|
||||||
@@ -1180,7 +1196,7 @@
|
|||||||
},
|
},
|
||||||
"OTHER_PROVIDERS": {
|
"OTHER_PROVIDERS": {
|
||||||
"TITLE": "Other Providers",
|
"TITLE": "Other Providers",
|
||||||
"DESCRIPTION": "Connect with Other Providers"
|
"DESCRIPTION": "Use IMAP/SMTP"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"CHANNELS": {
|
"CHANNELS": {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup>
|
<script setup>
|
||||||
import { ref, computed, onMounted } from 'vue';
|
import { computed } from 'vue';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { useRouter } from 'vue-router';
|
import { useRouter } from 'vue-router';
|
||||||
import { useMapGetter } from 'dashboard/composables/store';
|
import { useMapGetter } from 'dashboard/composables/store';
|
||||||
@@ -14,12 +14,12 @@ const { accountId, currentAccount } = useAccount();
|
|||||||
|
|
||||||
const globalConfig = useMapGetter('globalConfig/get');
|
const globalConfig = useMapGetter('globalConfig/get');
|
||||||
|
|
||||||
const enabledFeatures = ref({});
|
|
||||||
|
|
||||||
const hasTiktokConfigured = computed(() => {
|
const hasTiktokConfigured = computed(() => {
|
||||||
return window.chatwootConfig?.tiktokAppId;
|
return window.chatwootConfig?.tiktokAppId;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const enabledFeatures = computed(() => currentAccount.value?.features || {});
|
||||||
|
|
||||||
const channelList = computed(() => {
|
const channelList = computed(() => {
|
||||||
const { apiChannelName } = globalConfig.value;
|
const { apiChannelName } = globalConfig.value;
|
||||||
const channels = [
|
const channels = [
|
||||||
@@ -105,10 +105,6 @@ const channelList = computed(() => {
|
|||||||
return channels;
|
return channels;
|
||||||
});
|
});
|
||||||
|
|
||||||
const initializeEnabledFeatures = async () => {
|
|
||||||
enabledFeatures.value = currentAccount.value.features;
|
|
||||||
};
|
|
||||||
|
|
||||||
const initChannelAuth = channel => {
|
const initChannelAuth = channel => {
|
||||||
const params = {
|
const params = {
|
||||||
sub_page: channel,
|
sub_page: channel,
|
||||||
@@ -116,10 +112,6 @@ const initChannelAuth = channel => {
|
|||||||
};
|
};
|
||||||
router.push({ name: 'settings_inboxes_page_channel', params });
|
router.push({ name: 'settings_inboxes_page_channel', params });
|
||||||
};
|
};
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
initializeEnabledFeatures();
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { mapGetters } from 'vuex';
|
import { mapGetters } from 'vuex';
|
||||||
import { useAlert } from 'dashboard/composables';
|
import { useAlert } from 'dashboard/composables';
|
||||||
import SettingsFieldSection from 'dashboard/components-next/Settings/SettingsFieldSection.vue';
|
import SettingsToggleSection from 'dashboard/components-next/Settings/SettingsToggleSection.vue';
|
||||||
import { useVuelidate } from '@vuelidate/core';
|
import { useVuelidate } from '@vuelidate/core';
|
||||||
import { required, minLength } from '@vuelidate/validators';
|
import { required, minLength } from '@vuelidate/validators';
|
||||||
import InputRadioGroup from './components/InputRadioGroup.vue';
|
import InputRadioGroup from './components/InputRadioGroup.vue';
|
||||||
@@ -10,7 +10,7 @@ import NextButton from 'dashboard/components-next/button/Button.vue';
|
|||||||
|
|
||||||
export default {
|
export default {
|
||||||
components: {
|
components: {
|
||||||
SettingsFieldSection,
|
SettingsToggleSection,
|
||||||
InputRadioGroup,
|
InputRadioGroup,
|
||||||
SingleSelectDropdown,
|
SingleSelectDropdown,
|
||||||
NextButton,
|
NextButton,
|
||||||
@@ -157,91 +157,88 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<SettingsFieldSection
|
<form class="flex flex-col gap-4" @submit.prevent="updateInbox">
|
||||||
:label="$t('INBOX_MGMT.SMTP.TITLE')"
|
<SettingsToggleSection
|
||||||
:help-text="$t('INBOX_MGMT.SMTP.SUBTITLE')"
|
v-model="isSMTPEnabled"
|
||||||
class="[&>div]:!items-start [&>div>label]:mt-1 mb-4"
|
:header="$t('INBOX_MGMT.SMTP.TOGGLE_AVAILABILITY')"
|
||||||
>
|
:description="$t('INBOX_MGMT.SMTP.TOGGLE_HELP')"
|
||||||
<form @submit.prevent="updateInbox">
|
>
|
||||||
<label for="toggle-enable-smtp">
|
<template v-if="isSMTPEnabled" #editor>
|
||||||
<input
|
<div class="pt-2">
|
||||||
v-model="isSMTPEnabled"
|
<p class="mb-4 text-sm text-n-slate-11">
|
||||||
type="checkbox"
|
{{ $t('INBOX_MGMT.SMTP.SUBTITLE') }}
|
||||||
name="toggle-enable-smtp"
|
</p>
|
||||||
class="ltr:mr-1 rtl:ml-1"
|
<woot-input
|
||||||
/>
|
v-model="address"
|
||||||
{{ $t('INBOX_MGMT.SMTP.TOGGLE_AVAILABILITY') }}
|
:class="{ error: v$.address.$error }"
|
||||||
</label>
|
class="w-full"
|
||||||
<p>{{ $t('INBOX_MGMT.SMTP.TOGGLE_HELP') }}</p>
|
:label="$t('INBOX_MGMT.SMTP.ADDRESS.LABEL')"
|
||||||
<div v-if="isSMTPEnabled" class="mb-6">
|
:placeholder="$t('INBOX_MGMT.SMTP.ADDRESS.PLACE_HOLDER')"
|
||||||
<woot-input
|
@blur="v$.address.$touch"
|
||||||
v-model="address"
|
/>
|
||||||
:class="{ error: v$.address.$error }"
|
<woot-input
|
||||||
class="w-full"
|
v-model="port"
|
||||||
:label="$t('INBOX_MGMT.SMTP.ADDRESS.LABEL')"
|
type="number"
|
||||||
:placeholder="$t('INBOX_MGMT.SMTP.ADDRESS.PLACE_HOLDER')"
|
:class="{ error: v$.port.$error }"
|
||||||
@blur="v$.address.$touch"
|
class="w-full"
|
||||||
/>
|
:label="$t('INBOX_MGMT.SMTP.PORT.LABEL')"
|
||||||
<woot-input
|
:placeholder="$t('INBOX_MGMT.SMTP.PORT.PLACE_HOLDER')"
|
||||||
v-model="port"
|
@blur="v$.port.$touch"
|
||||||
type="number"
|
/>
|
||||||
:class="{ error: v$.port.$error }"
|
<woot-input
|
||||||
class="w-full"
|
v-model="login"
|
||||||
:label="$t('INBOX_MGMT.SMTP.PORT.LABEL')"
|
:class="{ error: v$.login.$error }"
|
||||||
:placeholder="$t('INBOX_MGMT.SMTP.PORT.PLACE_HOLDER')"
|
class="w-full"
|
||||||
@blur="v$.port.$touch"
|
:label="$t('INBOX_MGMT.SMTP.LOGIN.LABEL')"
|
||||||
/>
|
:placeholder="$t('INBOX_MGMT.SMTP.LOGIN.PLACE_HOLDER')"
|
||||||
<woot-input
|
@blur="v$.login.$touch"
|
||||||
v-model="login"
|
/>
|
||||||
:class="{ error: v$.login.$error }"
|
<woot-input
|
||||||
class="w-full"
|
v-model="password"
|
||||||
:label="$t('INBOX_MGMT.SMTP.LOGIN.LABEL')"
|
:class="{ error: v$.password.$error }"
|
||||||
:placeholder="$t('INBOX_MGMT.SMTP.LOGIN.PLACE_HOLDER')"
|
class="w-full"
|
||||||
@blur="v$.login.$touch"
|
:label="$t('INBOX_MGMT.SMTP.PASSWORD.LABEL')"
|
||||||
/>
|
:placeholder="$t('INBOX_MGMT.SMTP.PASSWORD.PLACE_HOLDER')"
|
||||||
<woot-input
|
type="password"
|
||||||
v-model="password"
|
@blur="v$.password.$touch"
|
||||||
:class="{ error: v$.password.$error }"
|
/>
|
||||||
class="w-full"
|
<woot-input
|
||||||
:label="$t('INBOX_MGMT.SMTP.PASSWORD.LABEL')"
|
v-model="domain"
|
||||||
:placeholder="$t('INBOX_MGMT.SMTP.PASSWORD.PLACE_HOLDER')"
|
:class="{ error: v$.domain.$error }"
|
||||||
type="password"
|
class="w-full"
|
||||||
@blur="v$.password.$touch"
|
:label="$t('INBOX_MGMT.SMTP.DOMAIN.LABEL')"
|
||||||
/>
|
:placeholder="$t('INBOX_MGMT.SMTP.DOMAIN.PLACE_HOLDER')"
|
||||||
<woot-input
|
@blur="v$.domain.$touch"
|
||||||
v-model="domain"
|
/>
|
||||||
:class="{ error: v$.domain.$error }"
|
<InputRadioGroup
|
||||||
class="w-full"
|
:label="$t('INBOX_MGMT.SMTP.ENCRYPTION')"
|
||||||
:label="$t('INBOX_MGMT.SMTP.DOMAIN.LABEL')"
|
:items="encryptionProtocols"
|
||||||
:placeholder="$t('INBOX_MGMT.SMTP.DOMAIN.PLACE_HOLDER')"
|
:action="handleEncryptionChange"
|
||||||
@blur="v$.domain.$touch"
|
/>
|
||||||
/>
|
<SingleSelectDropdown
|
||||||
<InputRadioGroup
|
class="w-full"
|
||||||
:label="$t('INBOX_MGMT.SMTP.ENCRYPTION')"
|
:label="$t('INBOX_MGMT.SMTP.OPEN_SSL_VERIFY_MODE')"
|
||||||
:items="encryptionProtocols"
|
:selected="openSSLVerifyMode"
|
||||||
:action="handleEncryptionChange"
|
:options="openSSLVerifyModes"
|
||||||
/>
|
:action="handleSSLModeChange"
|
||||||
<SingleSelectDropdown
|
/>
|
||||||
class="w-full"
|
<SingleSelectDropdown
|
||||||
:label="$t('INBOX_MGMT.SMTP.OPEN_SSL_VERIFY_MODE')"
|
class="w-full"
|
||||||
:selected="openSSLVerifyMode"
|
:label="$t('INBOX_MGMT.SMTP.AUTH_MECHANISM')"
|
||||||
:options="openSSLVerifyModes"
|
:selected="authMechanism"
|
||||||
:action="handleSSLModeChange"
|
:options="authMechanisms"
|
||||||
/>
|
:action="handleAuthMechanismChange"
|
||||||
<SingleSelectDropdown
|
/>
|
||||||
class="w-full"
|
</div>
|
||||||
:label="$t('INBOX_MGMT.SMTP.AUTH_MECHANISM')"
|
</template>
|
||||||
:selected="authMechanism"
|
</SettingsToggleSection>
|
||||||
:options="authMechanisms"
|
<div class="flex justify-end">
|
||||||
:action="handleAuthMechanismChange"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<NextButton
|
<NextButton
|
||||||
type="submit"
|
type="submit"
|
||||||
:label="$t('INBOX_MGMT.SMTP.UPDATE')"
|
:label="$t('INBOX_MGMT.SMTP.UPDATE')"
|
||||||
:is-loading="uiFlags.isUpdatingSMTP"
|
:is-loading="uiFlags.isUpdatingSMTP"
|
||||||
:disabled="(v$.$invalid && isSMTPEnabled) || uiFlags.isUpdatingSMTP"
|
:disabled="(v$.$invalid && isSMTPEnabled) || uiFlags.isUpdatingSMTP"
|
||||||
/>
|
/>
|
||||||
</form>
|
</div>
|
||||||
</SettingsFieldSection>
|
</form>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
@@ -3,9 +3,12 @@ import { ref, computed } from 'vue';
|
|||||||
import ForwardToOption from './emailChannels/ForwardToOption.vue';
|
import ForwardToOption from './emailChannels/ForwardToOption.vue';
|
||||||
import Microsoft from './emailChannels/Microsoft.vue';
|
import Microsoft from './emailChannels/Microsoft.vue';
|
||||||
import Google from './emailChannels/Google.vue';
|
import Google from './emailChannels/Google.vue';
|
||||||
|
import ImapSmtpOption from './emailChannels/ImapSmtpOption.vue';
|
||||||
import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
|
import ChannelSelector from 'dashboard/components/ChannelSelector.vue';
|
||||||
import PageHeader from '../../SettingsSubPageHeader.vue';
|
import PageHeader from '../../SettingsSubPageHeader.vue';
|
||||||
|
|
||||||
|
import { FEATURE_FLAGS } from 'dashboard/featureFlags';
|
||||||
|
import { useAccount } from 'dashboard/composables/useAccount';
|
||||||
import { useStoreGetters } from 'dashboard/composables/store';
|
import { useStoreGetters } from 'dashboard/composables/store';
|
||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
|
|
||||||
@@ -13,10 +16,20 @@ const provider = ref('');
|
|||||||
|
|
||||||
const getters = useStoreGetters();
|
const getters = useStoreGetters();
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
const { currentAccount } = useAccount();
|
||||||
|
|
||||||
const globalConfig = getters['globalConfig/get'];
|
const globalConfig = getters['globalConfig/get'];
|
||||||
const isAChatwootInstance = getters['globalConfig/isAChatwootInstance'];
|
const isAChatwootInstance = getters['globalConfig/isAChatwootInstance'];
|
||||||
|
|
||||||
|
const isInboundEmailEnabled = computed(
|
||||||
|
() => currentAccount.value?.features?.[FEATURE_FLAGS.INBOUND_EMAILS]
|
||||||
|
);
|
||||||
|
|
||||||
|
const isForwardingEnabled = computed(
|
||||||
|
() =>
|
||||||
|
isInboundEmailEnabled.value && globalConfig.value.inboundEmailDomainPresent
|
||||||
|
);
|
||||||
|
|
||||||
const emailProviderList = computed(() => {
|
const emailProviderList = computed(() => {
|
||||||
return [
|
return [
|
||||||
{
|
{
|
||||||
@@ -33,6 +46,16 @@ const emailProviderList = computed(() => {
|
|||||||
key: 'google',
|
key: 'google',
|
||||||
icon: 'i-woot-gmail',
|
icon: 'i-woot-gmail',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: t('INBOX_MGMT.ADD.EMAIL_CHANNEL.SETUP_OPTIONS.FORWARDING.TITLE'),
|
||||||
|
description: t(
|
||||||
|
'INBOX_MGMT.ADD.EMAIL_CHANNEL.SETUP_OPTIONS.FORWARDING.DESCRIPTION'
|
||||||
|
),
|
||||||
|
isEnabled: isForwardingEnabled.value,
|
||||||
|
key: 'forwarding',
|
||||||
|
icon: 'i-lucide-forward',
|
||||||
|
showWhenDisabled: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS.TITLE'),
|
title: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS.TITLE'),
|
||||||
description: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS.DESCRIPTION'),
|
description: t('INBOX_MGMT.EMAIL_PROVIDERS.OTHER_PROVIDERS.DESCRIPTION'),
|
||||||
@@ -41,6 +64,10 @@ const emailProviderList = computed(() => {
|
|||||||
icon: 'i-woot-mail',
|
icon: 'i-woot-mail',
|
||||||
},
|
},
|
||||||
].filter(providerConfig => {
|
].filter(providerConfig => {
|
||||||
|
if (providerConfig.showWhenDisabled) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
if (isAChatwootInstance.value) {
|
if (isAChatwootInstance.value) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -76,5 +103,6 @@ function onClick(emailProvider) {
|
|||||||
</div>
|
</div>
|
||||||
<Microsoft v-else-if="provider === 'microsoft'" />
|
<Microsoft v-else-if="provider === 'microsoft'" />
|
||||||
<Google v-else-if="provider === 'google'" />
|
<Google v-else-if="provider === 'google'" />
|
||||||
<ForwardToOption v-else-if="provider === 'other_provider'" />
|
<ForwardToOption v-else-if="provider === 'forwarding'" />
|
||||||
|
<ImapSmtpOption v-else-if="provider === 'other_provider'" />
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+21
-11
@@ -15,31 +15,41 @@ const props = defineProps({
|
|||||||
|
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const isImapSmtpInbox = computed(() => {
|
||||||
|
return props.inbox.imap_enabled;
|
||||||
|
});
|
||||||
|
|
||||||
const message = computed(() => {
|
const message = computed(() => {
|
||||||
return props.inbox.forwarding_enabled
|
return isImapSmtpInbox.value
|
||||||
? t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE')
|
? t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE_IMAP_SMTP')
|
||||||
: t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE_NO_FORWARDING');
|
: t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FINISH_MESSAGE_FORWARDING');
|
||||||
});
|
});
|
||||||
|
|
||||||
const showForwardingAddress = computed(() => {
|
const showForwardingAddress = computed(() => {
|
||||||
return props.inbox.forwarding_enabled;
|
return !isImapSmtpInbox.value && props.inbox.forwarding_enabled;
|
||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<div class="w-full text-center">
|
<div class="w-full text-center flex flex-col items-center">
|
||||||
<p class="text-base text-n-slate-11 mt-4 w-4/5 mx-auto leading-7">
|
<p class="text-base text-n-slate-11 mt-4 max-w-2xl leading-7">
|
||||||
{{ message }}
|
{{ message }}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div v-if="showForwardingAddress" class="w-[50%] max-w-[50%] mx-auto">
|
<div v-if="showForwardingAddress" class="w-full max-w-xl mt-8">
|
||||||
<p class="mt-8 mb-4 font-medium text-n-slate-11">
|
<p class="mb-4 font-medium text-n-slate-11">
|
||||||
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FORWARDING_ADDRESS_LABEL') }}
|
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FORWARDING_ADDRESS_LABEL') }}
|
||||||
</p>
|
</p>
|
||||||
<woot-code lang="html" :script="inbox.forward_to_email" />
|
<woot-code lang="html" :script="inbox.forward_to_email" />
|
||||||
|
<p class="mt-4 text-sm text-n-slate-11 max-w-md mx-auto leading-6">
|
||||||
|
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FORWARDING_RULE_HELP') }}
|
||||||
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<p class="mt-8 text-sm text-n-slate-11 pb-4">
|
<p
|
||||||
|
v-if="isImapSmtpInbox"
|
||||||
|
class="mt-8 text-sm text-n-slate-11 pb-4 max-w-xl leading-6"
|
||||||
|
>
|
||||||
<router-link
|
<router-link
|
||||||
:to="{
|
:to="{
|
||||||
name: 'settings_inbox_show',
|
name: 'settings_inbox_show',
|
||||||
@@ -47,9 +57,9 @@ const showForwardingAddress = computed(() => {
|
|||||||
}"
|
}"
|
||||||
class="text-n-woot-600 hover:text-n-woot-700 underline"
|
class="text-n-woot-600 hover:text-n-woot-700 underline"
|
||||||
>
|
>
|
||||||
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CONFIGURE_SMTP_IMAP_LINK') }}
|
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CONFIGURE_EMAIL_SETTINGS_LINK') }}
|
||||||
</router-link>
|
</router-link>
|
||||||
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CONFIGURE_SMTP_IMAP_TEXT') }}
|
{{ $t('INBOX_MGMT.ADD.EMAIL_CHANNEL.MANAGE_SMTP_IMAP_TEXT') }}
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|||||||
+355
@@ -0,0 +1,355 @@
|
|||||||
|
<script setup>
|
||||||
|
import { computed, reactive, watch } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
import { useVuelidate } from '@vuelidate/core';
|
||||||
|
import {
|
||||||
|
email as emailRule,
|
||||||
|
required,
|
||||||
|
requiredIf,
|
||||||
|
} from '@vuelidate/validators';
|
||||||
|
import { useAlert } from 'dashboard/composables';
|
||||||
|
import { useMapGetter, useStore } from 'dashboard/composables/store';
|
||||||
|
|
||||||
|
import PageHeader from '../../../SettingsSubPageHeader.vue';
|
||||||
|
import Input from 'dashboard/components-next/input/Input.vue';
|
||||||
|
import Select from 'dashboard/components-next/select/Select.vue';
|
||||||
|
import NextButton from 'dashboard/components-next/button/Button.vue';
|
||||||
|
|
||||||
|
const IMAP_AUTH_OPTIONS = ['plain', 'login', 'cram-md5'];
|
||||||
|
const SMTP_AUTH_OPTIONS = [
|
||||||
|
'plain',
|
||||||
|
'login',
|
||||||
|
'cram-md5',
|
||||||
|
'xoauth',
|
||||||
|
'xoauth2',
|
||||||
|
'ntlm',
|
||||||
|
'gssapi',
|
||||||
|
];
|
||||||
|
const SMTP_VERIFY_OPTIONS = [
|
||||||
|
'none',
|
||||||
|
'peer',
|
||||||
|
'client_once',
|
||||||
|
'fail_if_no_peer_cert',
|
||||||
|
];
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const router = useRouter();
|
||||||
|
const store = useStore();
|
||||||
|
const uiFlags = useMapGetter('inboxes/getUIFlags');
|
||||||
|
|
||||||
|
const state = reactive({
|
||||||
|
channelName: '',
|
||||||
|
email: '',
|
||||||
|
imapAddress: '',
|
||||||
|
imapPort: 993,
|
||||||
|
imapLogin: '',
|
||||||
|
imapPassword: '',
|
||||||
|
imapEnableSSL: true,
|
||||||
|
imapAuthentication: 'plain',
|
||||||
|
imapFetchInterval: 1,
|
||||||
|
smtpAddress: '',
|
||||||
|
smtpPort: 587,
|
||||||
|
smtpLogin: '',
|
||||||
|
smtpPassword: '',
|
||||||
|
smtpDomain: '',
|
||||||
|
smtpEncryption: 'starttls',
|
||||||
|
smtpVerifyMode: 'none',
|
||||||
|
smtpAuthentication: 'login',
|
||||||
|
});
|
||||||
|
|
||||||
|
const selectOptions = options =>
|
||||||
|
options.map(value => ({ label: value, value }));
|
||||||
|
|
||||||
|
const importWindowOptions = computed(() => [
|
||||||
|
{
|
||||||
|
value: 1,
|
||||||
|
label: t('INBOX_MGMT.ADD.EMAIL_CHANNEL.IMPORT_OPTIONS.ONE_DAY'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 7,
|
||||||
|
label: t('INBOX_MGMT.ADD.EMAIL_CHANNEL.IMPORT_OPTIONS.SEVEN_DAYS'),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
value: 30,
|
||||||
|
label: t('INBOX_MGMT.ADD.EMAIL_CHANNEL.IMPORT_OPTIONS.THIRTY_DAYS'),
|
||||||
|
},
|
||||||
|
]);
|
||||||
|
|
||||||
|
const isSMTPConfigured = computed(() =>
|
||||||
|
[
|
||||||
|
state.smtpAddress,
|
||||||
|
state.smtpLogin,
|
||||||
|
state.smtpPassword,
|
||||||
|
state.smtpDomain,
|
||||||
|
].some(value => value?.trim())
|
||||||
|
);
|
||||||
|
|
||||||
|
const smtpRequired = requiredIf(() => isSMTPConfigured.value);
|
||||||
|
|
||||||
|
const validationRules = {
|
||||||
|
channelName: { required },
|
||||||
|
email: { required, email: emailRule },
|
||||||
|
imapAddress: { required },
|
||||||
|
imapPort: { required },
|
||||||
|
imapLogin: { required },
|
||||||
|
imapPassword: { required },
|
||||||
|
smtpAddress: { required: smtpRequired },
|
||||||
|
smtpPort: { required: smtpRequired },
|
||||||
|
smtpLogin: { required: smtpRequired },
|
||||||
|
smtpPassword: { required: smtpRequired },
|
||||||
|
smtpDomain: { required: smtpRequired },
|
||||||
|
};
|
||||||
|
|
||||||
|
const v$ = useVuelidate(validationRules, state);
|
||||||
|
const fieldState = field => (v$.value[field]?.$error ? 'error' : 'info');
|
||||||
|
|
||||||
|
watch(
|
||||||
|
() => state.email,
|
||||||
|
value => {
|
||||||
|
state.imapLogin = state.imapLogin || value;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
const buildChannelPayload = () => ({
|
||||||
|
type: 'email',
|
||||||
|
email: state.email,
|
||||||
|
imap_enabled: true,
|
||||||
|
imap_address: state.imapAddress,
|
||||||
|
imap_port: state.imapPort,
|
||||||
|
imap_login: state.imapLogin,
|
||||||
|
imap_password: state.imapPassword,
|
||||||
|
imap_enable_ssl: state.imapEnableSSL,
|
||||||
|
imap_authentication: state.imapAuthentication,
|
||||||
|
smtp_enabled: isSMTPConfigured.value,
|
||||||
|
smtp_address: state.smtpAddress,
|
||||||
|
smtp_port: state.smtpPort,
|
||||||
|
smtp_login: state.smtpLogin,
|
||||||
|
smtp_password: state.smtpPassword,
|
||||||
|
smtp_domain: state.smtpDomain,
|
||||||
|
smtp_enable_ssl_tls: state.smtpEncryption === 'ssl',
|
||||||
|
smtp_enable_starttls_auto: state.smtpEncryption === 'starttls',
|
||||||
|
smtp_openssl_verify_mode: state.smtpVerifyMode,
|
||||||
|
smtp_authentication: state.smtpAuthentication,
|
||||||
|
});
|
||||||
|
|
||||||
|
async function createChannel() {
|
||||||
|
const isValid = await v$.value.$validate();
|
||||||
|
if (!isValid) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const emailChannel = await store.dispatch('inboxes/createChannel', {
|
||||||
|
name: state.channelName.trim(),
|
||||||
|
imap_fetch_interval: state.imapFetchInterval,
|
||||||
|
channel: buildChannelPayload(),
|
||||||
|
});
|
||||||
|
|
||||||
|
router.replace({
|
||||||
|
name: 'settings_inboxes_add_agents',
|
||||||
|
params: { page: 'new', inbox_id: emailChannel.id },
|
||||||
|
});
|
||||||
|
} catch (error) {
|
||||||
|
useAlert(
|
||||||
|
error?.message || t('INBOX_MGMT.ADD.EMAIL_CHANNEL.API.ERROR_MESSAGE')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<div class="overflow-auto col-span-6 p-6 w-full h-full">
|
||||||
|
<PageHeader
|
||||||
|
:header-title="t('INBOX_MGMT.ADD.EMAIL_CHANNEL.TITLE')"
|
||||||
|
:header-content="t('INBOX_MGMT.ADD.EMAIL_CHANNEL.DESC')"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<form class="max-w-3xl" @submit.prevent="createChannel">
|
||||||
|
<div class="grid grid-cols-1 gap-x-4 md:grid-cols-2">
|
||||||
|
<Input
|
||||||
|
v-model="state.channelName"
|
||||||
|
:label="t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.LABEL')"
|
||||||
|
:placeholder="
|
||||||
|
t('INBOX_MGMT.ADD.EMAIL_CHANNEL.CHANNEL_NAME.PLACEHOLDER')
|
||||||
|
"
|
||||||
|
:message-type="fieldState('channelName')"
|
||||||
|
@blur="v$.channelName.$touch"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="state.email"
|
||||||
|
:label="t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.ADD.EMAIL_CHANNEL.EMAIL.PLACEHOLDER')"
|
||||||
|
:message-type="fieldState('email')"
|
||||||
|
@blur="v$.email.$touch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<section class="pt-5 mt-6 border-t border-n-weak">
|
||||||
|
<h3 class="mb-1 text-sm font-medium text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.IMAP.TITLE') }}
|
||||||
|
</h3>
|
||||||
|
<p class="mb-4 text-sm text-n-slate-11">
|
||||||
|
{{ t('INBOX_MGMT.IMAP.CREATE_HELP') }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-x-4 gap-y-4 md:grid-cols-2">
|
||||||
|
<Input
|
||||||
|
v-model="state.imapAddress"
|
||||||
|
:label="t('INBOX_MGMT.IMAP.ADDRESS.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.IMAP.ADDRESS.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('imapAddress')"
|
||||||
|
@blur="v$.imapAddress.$touch"
|
||||||
|
/>
|
||||||
|
<div class="flex flex-col gap-2">
|
||||||
|
<Input
|
||||||
|
v-model="state.imapPort"
|
||||||
|
type="number"
|
||||||
|
:label="t('INBOX_MGMT.IMAP.PORT.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.IMAP.PORT.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('imapPort')"
|
||||||
|
@blur="v$.imapPort.$touch"
|
||||||
|
/>
|
||||||
|
<label
|
||||||
|
for="imap-enable-ssl"
|
||||||
|
class="flex items-center gap-2 text-sm font-medium text-n-slate-12"
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="imap-enable-ssl"
|
||||||
|
v-model="state.imapEnableSSL"
|
||||||
|
type="checkbox"
|
||||||
|
/>
|
||||||
|
{{ t('INBOX_MGMT.IMAP.ENABLE_SSL') }}
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
<Input
|
||||||
|
v-model="state.imapLogin"
|
||||||
|
:label="t('INBOX_MGMT.IMAP.LOGIN.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.IMAP.LOGIN.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('imapLogin')"
|
||||||
|
@blur="v$.imapLogin.$touch"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="state.imapPassword"
|
||||||
|
type="password"
|
||||||
|
:label="t('INBOX_MGMT.IMAP.PASSWORD.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.IMAP.PASSWORD.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('imapPassword')"
|
||||||
|
@blur="v$.imapPassword.$touch"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-x-4 gap-y-4 mt-4 md:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1">
|
||||||
|
<span class="text-heading-3 text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.IMAP.AUTH_MECHANISM') }}
|
||||||
|
</span>
|
||||||
|
<Select
|
||||||
|
v-model="state.imapAuthentication"
|
||||||
|
:options="selectOptions(IMAP_AUTH_OPTIONS)"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1">
|
||||||
|
<span class="text-heading-3 text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.ADD.EMAIL_CHANNEL.FETCH_EMAILS_FROM') }}
|
||||||
|
</span>
|
||||||
|
<Select
|
||||||
|
v-model="state.imapFetchInterval"
|
||||||
|
:options="importWindowOptions"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section class="pt-5 mt-6 border-t border-n-weak">
|
||||||
|
<h3 class="mb-1 text-sm font-medium text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.SMTP.TITLE') }}
|
||||||
|
</h3>
|
||||||
|
<p class="mb-4 text-sm text-n-slate-11">
|
||||||
|
{{ t('INBOX_MGMT.SMTP.CREATE_HELP') }}
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-x-4 gap-y-4 md:grid-cols-2">
|
||||||
|
<Input
|
||||||
|
v-model="state.smtpAddress"
|
||||||
|
:label="t('INBOX_MGMT.SMTP.ADDRESS.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.SMTP.ADDRESS.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('smtpAddress')"
|
||||||
|
@blur="v$.smtpAddress.$touch"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="state.smtpPort"
|
||||||
|
type="number"
|
||||||
|
:label="t('INBOX_MGMT.SMTP.PORT.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.SMTP.PORT.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('smtpPort')"
|
||||||
|
@blur="v$.smtpPort.$touch"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="state.smtpLogin"
|
||||||
|
:label="t('INBOX_MGMT.SMTP.LOGIN.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.SMTP.LOGIN.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('smtpLogin')"
|
||||||
|
@blur="v$.smtpLogin.$touch"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="state.smtpPassword"
|
||||||
|
type="password"
|
||||||
|
:label="t('INBOX_MGMT.SMTP.PASSWORD.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.SMTP.PASSWORD.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('smtpPassword')"
|
||||||
|
@blur="v$.smtpPassword.$touch"
|
||||||
|
/>
|
||||||
|
<Input
|
||||||
|
v-model="state.smtpDomain"
|
||||||
|
:label="t('INBOX_MGMT.SMTP.DOMAIN.LABEL')"
|
||||||
|
:placeholder="t('INBOX_MGMT.SMTP.DOMAIN.PLACE_HOLDER')"
|
||||||
|
:message-type="fieldState('smtpDomain')"
|
||||||
|
@blur="v$.smtpDomain.$touch"
|
||||||
|
/>
|
||||||
|
<label class="flex flex-col gap-1">
|
||||||
|
<span class="text-heading-3 text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.SMTP.AUTH_MECHANISM') }}
|
||||||
|
</span>
|
||||||
|
<Select
|
||||||
|
v-model="state.smtpAuthentication"
|
||||||
|
:options="selectOptions(SMTP_AUTH_OPTIONS)"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-1 gap-x-4 gap-y-4 mt-4 md:grid-cols-2">
|
||||||
|
<label class="flex flex-col gap-1">
|
||||||
|
<span class="text-heading-3 text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.SMTP.ENCRYPTION') }}
|
||||||
|
</span>
|
||||||
|
<Select
|
||||||
|
v-model="state.smtpEncryption"
|
||||||
|
:options="[
|
||||||
|
{ value: 'starttls', label: t('INBOX_MGMT.SMTP.START_TLS') },
|
||||||
|
{ value: 'ssl', label: t('INBOX_MGMT.SMTP.SSL_TLS') },
|
||||||
|
]"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<label class="flex flex-col gap-1">
|
||||||
|
<span class="text-heading-3 text-n-slate-12">
|
||||||
|
{{ t('INBOX_MGMT.SMTP.OPEN_SSL_VERIFY_MODE') }}
|
||||||
|
</span>
|
||||||
|
<Select
|
||||||
|
v-model="state.smtpVerifyMode"
|
||||||
|
:options="selectOptions(SMTP_VERIFY_OPTIONS)"
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<div class="w-full mt-6">
|
||||||
|
<NextButton
|
||||||
|
:is-loading="uiFlags.isCreating"
|
||||||
|
type="submit"
|
||||||
|
solid
|
||||||
|
blue
|
||||||
|
:label="t('INBOX_MGMT.ADD.EMAIL_CHANNEL.SUBMIT_BUTTON')"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
+12
-3
@@ -59,6 +59,9 @@ export default {
|
|||||||
isForwardingEnabled() {
|
isForwardingEnabled() {
|
||||||
return !!this.inbox.forwarding_enabled;
|
return !!this.inbox.forwarding_enabled;
|
||||||
},
|
},
|
||||||
|
isForwardingEmailInbox() {
|
||||||
|
return this.isForwardingEnabled && !this.inbox.imap_enabled;
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
inbox() {
|
inbox() {
|
||||||
@@ -331,7 +334,7 @@ export default {
|
|||||||
</SettingsFieldSection>
|
</SettingsFieldSection>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="isAnEmailChannel">
|
<div v-else-if="isAnEmailChannel">
|
||||||
<div>
|
<div v-if="isForwardingEmailInbox" class="flex flex-col gap-4">
|
||||||
<SettingsFieldSection
|
<SettingsFieldSection
|
||||||
:label="$t('INBOX_MGMT.SETTINGS_POPUP.FORWARD_EMAIL_TITLE')"
|
:label="$t('INBOX_MGMT.SETTINGS_POPUP.FORWARD_EMAIL_TITLE')"
|
||||||
:help-text="
|
:help-text="
|
||||||
@@ -354,8 +357,14 @@ export default {
|
|||||||
</div>
|
</div>
|
||||||
</SettingsFieldSection>
|
</SettingsFieldSection>
|
||||||
</div>
|
</div>
|
||||||
<ImapSettings :inbox="inbox" />
|
<ImapSettings v-if="!isForwardingEmailInbox" :inbox="inbox" />
|
||||||
<SmtpSettings v-if="inbox.imap_enabled" :inbox="inbox" />
|
<SmtpSettings
|
||||||
|
v-if="isForwardingEmailInbox || inbox.imap_enabled"
|
||||||
|
:inbox="inbox"
|
||||||
|
:class="{
|
||||||
|
'border-t border-n-weak pt-4 mt-2': isForwardingEmailInbox,
|
||||||
|
}"
|
||||||
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div v-else-if="isAWhatsAppChannel && !isATwilioChannel">
|
<div v-else-if="isAWhatsAppChannel && !isATwilioChannel">
|
||||||
<div v-if="inbox.provider_config">
|
<div v-if="inbox.provider_config">
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ const {
|
|||||||
MAXIMUM_FILE_UPLOAD_SIZE: maximumFileUploadSize,
|
MAXIMUM_FILE_UPLOAD_SIZE: maximumFileUploadSize,
|
||||||
HCAPTCHA_SITE_KEY: hCaptchaSiteKey,
|
HCAPTCHA_SITE_KEY: hCaptchaSiteKey,
|
||||||
INSTALLATION_NAME: installationName,
|
INSTALLATION_NAME: installationName,
|
||||||
|
INBOUND_EMAIL_DOMAIN_PRESENT: inboundEmailDomainPresent,
|
||||||
LOGO_THUMBNAIL: logoThumbnail,
|
LOGO_THUMBNAIL: logoThumbnail,
|
||||||
LOGO: logo,
|
LOGO: logo,
|
||||||
LOGO_DARK: logoDark,
|
LOGO_DARK: logoDark,
|
||||||
@@ -43,6 +44,7 @@ const state = {
|
|||||||
maximumFileUploadSize: resolveMaximumFileUploadSize(maximumFileUploadSize),
|
maximumFileUploadSize: resolveMaximumFileUploadSize(maximumFileUploadSize),
|
||||||
hCaptchaSiteKey,
|
hCaptchaSiteKey,
|
||||||
installationName,
|
installationName,
|
||||||
|
inboundEmailDomainPresent: parseBoolean(inboundEmailDomainPresent),
|
||||||
logo,
|
logo,
|
||||||
logoDark,
|
logoDark,
|
||||||
logoThumbnail,
|
logoThumbnail,
|
||||||
|
|||||||
@@ -79,8 +79,8 @@ end
|
|||||||
if resource.email?
|
if resource.email?
|
||||||
## Email Channel Attributes
|
## Email Channel Attributes
|
||||||
json.email resource.channel.try(:email)
|
json.email resource.channel.try(:email)
|
||||||
json.forwarding_enabled ENV.fetch('MAILER_INBOUND_EMAIL_DOMAIN', '').present?
|
json.forwarding_enabled resource.account.inbound_email_domain.present?
|
||||||
json.forward_to_email resource.channel.try(:forward_to_email) if ENV.fetch('MAILER_INBOUND_EMAIL_DOMAIN', '').present?
|
json.forward_to_email resource.channel.try(:forward_to_email) if resource.account.inbound_email_domain.present?
|
||||||
|
|
||||||
## IMAP
|
## IMAP
|
||||||
if Current.account_user&.administrator?
|
if Current.account_user&.administrator?
|
||||||
|
|||||||
@@ -399,6 +399,40 @@ RSpec.describe 'Inboxes API', type: :request do
|
|||||||
expect(response.body).to include('test@test.com')
|
expect(response.body).to include('test@test.com')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
it 'creates an email inbox with imap settings and queues the selected import window' do
|
||||||
|
imap_connection = instance_double(Net::IMAP, disconnected?: false)
|
||||||
|
|
||||||
|
allow(Net::IMAP).to receive(:new).and_return(imap_connection)
|
||||||
|
allow(imap_connection).to receive(:login)
|
||||||
|
allow(imap_connection).to receive(:disconnect)
|
||||||
|
|
||||||
|
expect do
|
||||||
|
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||||
|
headers: admin.create_new_auth_token,
|
||||||
|
params: {
|
||||||
|
name: 'Support',
|
||||||
|
imap_fetch_interval: 7,
|
||||||
|
channel: {
|
||||||
|
type: 'email',
|
||||||
|
email: 'support@example.com',
|
||||||
|
imap_enabled: true,
|
||||||
|
imap_address: 'imap.example.com',
|
||||||
|
imap_port: 993,
|
||||||
|
imap_login: 'support@example.com',
|
||||||
|
imap_password: 'imap-password',
|
||||||
|
imap_enable_ssl: true,
|
||||||
|
imap_authentication: 'login'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
as: :json
|
||||||
|
end.to have_enqueued_job(Inboxes::FetchImapEmailsJob).with(a_kind_of(Channel::Email), 7)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
channel = Channel::Email.find_by!(email: 'support@example.com')
|
||||||
|
expect(channel.imap_enabled).to be true
|
||||||
|
expect(channel.smtp_enabled).to be false
|
||||||
|
end
|
||||||
|
|
||||||
it 'creates an api inbox when administrator' do
|
it 'creates an api inbox when administrator' do
|
||||||
post "/api/v1/accounts/#{account.id}/inboxes",
|
post "/api/v1/accounts/#{account.id}/inboxes",
|
||||||
headers: admin.create_new_auth_token,
|
headers: admin.create_new_auth_token,
|
||||||
|
|||||||
Reference in New Issue
Block a user