chore: Allow more filetypes in uploads (#3557)
- Allowing the ability to upload more common file types like zip, Docx etc - Fallback for image bubble when the image URL isn't available fixes: #3270 Co-authored-by: Muhsin Keloth <muhsinkeramam@gmail.com> Co-authored-by: Nithin David <1277421+nithindavid@users.noreply.github.com>
This commit is contained in:
parent
76e8acd3c6
commit
6fe5484119
9 changed files with 99 additions and 20 deletions
|
@ -1,16 +1,29 @@
|
|||
module FileTypeHelper
|
||||
# NOTE: video, audio, image, etc are filetypes previewable in frontend
|
||||
def file_type(content_type)
|
||||
return :image if [
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/tiff',
|
||||
'image/bmp'
|
||||
].include?(content_type)
|
||||
|
||||
return :video if content_type.include?('video/')
|
||||
return :image if image_file?(content_type)
|
||||
return :video if video_file?(content_type)
|
||||
return :audio if content_type.include?('audio/')
|
||||
|
||||
:file
|
||||
end
|
||||
|
||||
def image_file?(content_type)
|
||||
[
|
||||
'image/jpeg',
|
||||
'image/png',
|
||||
'image/gif',
|
||||
'image/bmp',
|
||||
'image/webp'
|
||||
].include?(content_type)
|
||||
end
|
||||
|
||||
def video_file?(content_type)
|
||||
[
|
||||
'video/ogg',
|
||||
'video/mp4',
|
||||
'video/webm',
|
||||
'video/quicktime'
|
||||
].include?(content_type)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -45,6 +45,9 @@
|
|||
// 1. Global
|
||||
// ---------
|
||||
|
||||
// Disable contrast warnings in Foundation.
|
||||
$contrast-warnings: false;
|
||||
|
||||
$global-font-size: 10px;
|
||||
$global-width: 100%;
|
||||
$global-lineheight: 1.5;
|
||||
|
|
|
@ -15,7 +15,7 @@
|
|||
<file-upload
|
||||
ref="upload"
|
||||
:size="4096 * 4096"
|
||||
accept="image/png, image/jpeg, image/gif, image/bmp, image/tiff, application/pdf, audio/mpeg, video/mp4, audio/ogg, text/csv"
|
||||
:accept="allowedFileTypes"
|
||||
:drop="true"
|
||||
:drop-directory="false"
|
||||
@input-file="onFileUpload"
|
||||
|
@ -84,6 +84,7 @@ import {
|
|||
hasPressedAltAndAKey,
|
||||
} from 'shared/helpers/KeyboardHelpers';
|
||||
import eventListenerMixins from 'shared/mixins/eventListenerMixins';
|
||||
import { ALLOWED_FILE_TYPES } from 'shared/constants/messages';
|
||||
|
||||
import { REPLY_EDITOR_MODES } from './constants';
|
||||
export default {
|
||||
|
@ -161,6 +162,9 @@ export default {
|
|||
showAttachButton() {
|
||||
return this.showFileUpload || this.isNote;
|
||||
},
|
||||
allowedFileTypes() {
|
||||
return ALLOWED_FILE_TYPES;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
handleKeyEvents(e) {
|
||||
|
|
|
@ -24,9 +24,10 @@
|
|||
<div v-if="!isPending && hasAttachments">
|
||||
<div v-for="attachment in data.attachments" :key="attachment.id">
|
||||
<bubble-image
|
||||
v-if="attachment.file_type === 'image'"
|
||||
v-if="attachment.file_type === 'image' && !hasImageError"
|
||||
:url="attachment.data_url"
|
||||
:readable-time="readableTime"
|
||||
@error="onImageLoadError"
|
||||
/>
|
||||
<audio v-else-if="attachment.file_type === 'audio'" controls>
|
||||
<source :src="attachment.data_url" />
|
||||
|
@ -133,6 +134,7 @@ export default {
|
|||
data() {
|
||||
return {
|
||||
showContextMenu: false,
|
||||
hasImageError: false,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
|
@ -287,12 +289,20 @@ export default {
|
|||
return messageType ? 'right' : 'left';
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
data() {
|
||||
this.hasImageError = false;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.hasImageError = false;
|
||||
},
|
||||
methods: {
|
||||
hasMediaAttachment(type) {
|
||||
if (this.hasAttachments && this.data.attachments.length > 0) {
|
||||
const { attachments = [{}] } = this.data;
|
||||
const { file_type: fileType } = attachments[0];
|
||||
return fileType === type;
|
||||
return fileType === type && !this.hasImageError;
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
@ -317,6 +327,9 @@ export default {
|
|||
this.showAlert(this.$t('CONTACT_PANEL.COPY_SUCCESSFUL'));
|
||||
this.showContextMenu = false;
|
||||
},
|
||||
onImageLoadError() {
|
||||
this.hasImageError = true;
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
<template>
|
||||
<div class="image message-text__wrap">
|
||||
<img :src="url" @click="onClick" />
|
||||
<img :src="url" @click="onClick" @error="onImgError()" />
|
||||
<woot-modal :full-width="true" :show.sync="show" :on-close="onClose">
|
||||
<img :src="url" class="modal-image" />
|
||||
</woot-modal>
|
||||
|
@ -9,6 +9,7 @@
|
|||
|
||||
<script>
|
||||
export default {
|
||||
components: {},
|
||||
props: {
|
||||
url: {
|
||||
type: String,
|
||||
|
@ -27,6 +28,9 @@ export default {
|
|||
onClick() {
|
||||
this.show = true;
|
||||
},
|
||||
onImgError() {
|
||||
this.$emit('error');
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
|
|
@ -32,7 +32,11 @@
|
|||
:label="inboxNameLabel"
|
||||
:placeholder="inboxNamePlaceHolder"
|
||||
/>
|
||||
<label for="toggle-business-hours" class="toggle-input-wrap" v-if="isATwitterInbox">
|
||||
<label
|
||||
v-if="isATwitterInbox"
|
||||
for="toggle-business-hours"
|
||||
class="toggle-input-wrap"
|
||||
>
|
||||
<input
|
||||
v-model="tweetsEnabled"
|
||||
type="checkbox"
|
||||
|
|
|
@ -13,6 +13,17 @@ export const MESSAGE_TYPE = {
|
|||
// Size in mega bytes
|
||||
export const MAXIMUM_FILE_UPLOAD_SIZE = 40;
|
||||
|
||||
export const ALLOWED_FILE_TYPES =
|
||||
'image/*,' +
|
||||
'audio/*,' +
|
||||
'video/*,' +
|
||||
'.3gpp,' +
|
||||
'text/csv, text/plain, application/json, application/pdf, text/rtf,' +
|
||||
'application/zip, application/x-7z-compressed application/vnd.rar application/x-tar,' +
|
||||
'application/msword, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/vnd.oasis.opendocument.text,' +
|
||||
'application/vnd.openxmlformats-officedocument.presentationml.presentation, application/vnd.openxmlformats-officedocument.spreadsheetml.sheet,' +
|
||||
'application/vnd.openxmlformats-officedocument.wordprocessingml.document,';
|
||||
|
||||
export const CSAT_RATINGS = [
|
||||
{
|
||||
key: 'disappointed',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<template>
|
||||
<file-upload
|
||||
:size="4096 * 2048"
|
||||
accept="image/*, application/pdf, audio/mpeg, video/mp4, audio/ogg, text/csv"
|
||||
:accept="allowedFileTypes"
|
||||
@input-file="onFileUpload"
|
||||
>
|
||||
<button class="icon-button flex items-center justify-center">
|
||||
|
@ -15,7 +15,10 @@
|
|||
import FileUpload from 'vue-upload-component';
|
||||
import Spinner from 'shared/components/Spinner.vue';
|
||||
import { checkFileSizeLimit } from 'shared/helpers/FileHelper';
|
||||
import { MAXIMUM_FILE_UPLOAD_SIZE } from 'shared/constants/messages';
|
||||
import {
|
||||
MAXIMUM_FILE_UPLOAD_SIZE,
|
||||
ALLOWED_FILE_TYPES,
|
||||
} from 'shared/constants/messages';
|
||||
import { BUS_EVENTS } from 'shared/constants/busEvents';
|
||||
import FluentIcon from 'shared/components/FluentIcon/Index.vue';
|
||||
export default {
|
||||
|
@ -33,6 +36,9 @@ export default {
|
|||
fileUploadSizeLimit() {
|
||||
return MAXIMUM_FILE_UPLOAD_SIZE;
|
||||
},
|
||||
allowedFileTypes() {
|
||||
return ALLOWED_FILE_TYPES;
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
getFileType(fileType) {
|
||||
|
|
|
@ -17,6 +17,18 @@
|
|||
|
||||
class Attachment < ApplicationRecord
|
||||
include Rails.application.routes.url_helpers
|
||||
|
||||
ACCEPTABLE_FILE_TYPES = %w[
|
||||
text/csv text/plain text/rtf
|
||||
application/json application/pdf
|
||||
application/zip application/x-7z-compressed application/vnd.rar application/x-tar
|
||||
application/msword application/vnd.ms-excel application/vnd.ms-powerpoint application/rtf
|
||||
application/vnd.oasis.opendocument.text
|
||||
application/vnd.openxmlformats-officedocument.presentationml.presentation
|
||||
application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
|
||||
application/vnd.openxmlformats-officedocument.wordprocessingml.document
|
||||
].freeze
|
||||
|
||||
belongs_to :account
|
||||
belongs_to :message
|
||||
has_one_attached :file
|
||||
|
@ -90,10 +102,19 @@ class Attachment < ApplicationRecord
|
|||
def acceptable_file
|
||||
return unless should_validate_file?
|
||||
|
||||
errors.add(:file, 'is too big') if file.byte_size > 40.megabytes
|
||||
validate_file_size(file.byte_size)
|
||||
validate_file_content_type(file.content_type)
|
||||
end
|
||||
|
||||
acceptable_types = ['image/png', 'image/jpeg', 'image/gif', 'image/bmp', 'image/tiff', 'application/pdf', 'audio/mpeg', 'video/mp4', 'audio/ogg',
|
||||
'text/csv'].freeze
|
||||
errors.add(:file, 'filetype not supported') unless acceptable_types.include?(file.content_type)
|
||||
def validate_file_content_type(file_content_type)
|
||||
errors.add(:file, 'type not supported') unless media_file?(file_content_type) || ACCEPTABLE_FILE_TYPES.include?(file_content_type)
|
||||
end
|
||||
|
||||
def validate_file_size(byte_size)
|
||||
errors.add(:file, 'size is too big') if byte_size > 40.megabytes
|
||||
end
|
||||
|
||||
def media_file?(file_content_type)
|
||||
file_content_type.start_with?('image/', 'video/', 'audio/')
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in a new issue