diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee
index d70282853..36ac8f621 100644
--- a/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/article_view.coffee
@@ -82,6 +82,7 @@ class ArticleViewItem extends App.ObserverController
'click .textBubble a': 'stopPropagation'
'click .js-toggleFold': 'toggleFold'
'click .richtext-content img': 'imageView'
+ 'click .attachments img': 'imageView'
constructor: ->
super
@@ -178,9 +179,9 @@ class ArticleViewItem extends App.ObserverController
)
return
@html App.view('ticket_zoom/article_view')(
- ticket: @ticket
- article: article
- isCustomer: @permissionCheck('ticket.customer')
+ ticket: @ticket
+ article: article
+ isCustomer: @permissionCheck('ticket.customer')
)
new App.WidgetAvatar(
diff --git a/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_article_attachments.coffee b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_article_attachments.coffee
new file mode 100644
index 000000000..1d3ebe037
--- /dev/null
+++ b/app/assets/javascripts/app/controllers/ticket_zoom/sidebar_article_attachments.coffee
@@ -0,0 +1,34 @@
+class SidebarArticleAttachments extends App.Controller
+ sidebarItem: =>
+ return if !@Config.get('ui_ticket_zoom_sidebar_article_attachments')
+ @item = {
+ name: 'attachment'
+ badgeIcon: 'paperclip'
+ sidebarHead: 'Attachments'
+ sidebarCallback: @showObjects
+ sidebarActions: []
+ }
+ @item
+
+ showObjects: (el) =>
+ @el = el
+
+ if !@ticket || _.isEmpty(@ticket.article_ids)
+ @el.html("
#{App.i18n.translateInline('none')}
")
+ return
+ html = ''
+ for ticket_article_id, index in @ticket.article_ids
+ article = App.TicketArticle.find(ticket_article_id)
+ if article && article.attachments && !_.isEmpty(article.attachments)
+ html = App.view('ticket_zoom/sidebar_article_attachment')(article: article) + html
+ @el.html(html)
+ @el.delegate('.js-attachments img', 'click', (e) =>
+ @imageView(e)
+ )
+
+ imageView: (e) ->
+ e.preventDefault()
+ e.stopPropagation()
+ new App.TicketZoomArticleImageView(image: $(e.target).get(0).outerHTML)
+
+App.Config.set('900-ArticleAttachments', SidebarArticleAttachments, 'TicketZoomSidebar')
diff --git a/app/assets/javascripts/app/index.coffee b/app/assets/javascripts/app/index.coffee
index d9fbb2028..b6fdd1404 100644
--- a/app/assets/javascripts/app/index.coffee
+++ b/app/assets/javascripts/app/index.coffee
@@ -136,6 +136,43 @@ class App extends Spine.Controller
return marked(string)
App.i18n.translateContent(string)
+ ContentTypeIcon: (contentType) ->
+ icons =
+ # image
+ 'image/jpeg': 'file-image'
+ 'image/jpg': 'file-image'
+ 'image/png': 'file-image'
+ 'image/svg': 'file-image'
+ # documents
+ 'application/pdf': 'file-pdf'
+ 'application/msword': 'file-word' # .doc, .dot
+ 'application/vnd.ms-word': 'file-word'
+ 'application/vnd.oasis.opendocument.text': 'file-word'
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.document': 'file-word' # .docx
+ 'application/vnd.openxmlformats-officedocument.wordprocessingml.template': 'file-word' # .dotx
+ 'application/vnd.ms-excel': 'file-excel' # .xls
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet': 'file-excel' # .xlsx
+ 'application/vnd.oasis.opendocument.spreadsheet': 'file-excel'
+ 'application/vnd.ms-powerpoint': 'file-powerpoint' # .ppt
+ 'application/vnd.openxmlformats-officedocument.presentationml.presentation': 'file-powerpoint' # .pptx
+ 'application/vnd.oasis.opendocument.presentation': 'file-powerpoint'
+ 'text/plain': 'file-text'
+ 'text/html': 'file-code'
+ 'application/json': 'file-code'
+ 'message/rfc822': 'file-email'
+ # code
+ 'application/json': 'file-code'
+ # text
+ 'text/plain': 'file-text'
+ 'text/rtf': 'file-text'
+ # archives
+ 'application/gzip': 'file-archive'
+ 'application/zip': 'file-archive'
+ return icons[contentType]
+
+ canDownload: (contentType) ->
+ contentType != 'text/html'
+
@viewPrint: (object, attributeName, attributes, table) ->
if !attributes
attributes = {}
diff --git a/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco b/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco
index 2f553018d..a425f3913 100644
--- a/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco
+++ b/app/assets/javascripts/app/views/ticket_zoom/article_view.jst.eco
@@ -56,10 +56,27 @@
<%- @Icon('paperclip') %>
<%- @article.attachments.length %> <%- @T('Attached Files') %>
<% for attachment in @article.attachments: %>
-
+ <% if !@C('ui_ticket_zoom_attachments_preview'): %>
+
+ <% else: %>
+ download<% end %>>
+
+ <% if attachment.preferences && attachment.preferences['Content-Type'] && @ContentTypeIcon(attachment.preferences['Content-Type']): %>
+ <% if attachment.preferences['Content-Type'].match(/image\/(png|jpg|jpeg)/i): %>
+
+ <% else: %>
+ <%- @Icon( @ContentTypeIcon(attachment.preferences['Content-Type']) ) %>
+ <% end %>
+ <% else: %>
+ <%- @Icon('file-unknown') %>
+ <% end %>
+
+ <%= attachment.filename %>
+ <%- @humanFileSize(attachment.size) %>
+
+ <% end %>
<% end %>
<% end %>
diff --git a/app/assets/javascripts/app/views/ticket_zoom/sidebar_article_attachment.jst.eco b/app/assets/javascripts/app/views/ticket_zoom/sidebar_article_attachment.jst.eco
new file mode 100644
index 000000000..a49b2b0f5
--- /dev/null
+++ b/app/assets/javascripts/app/views/ticket_zoom/sidebar_article_attachment.jst.eco
@@ -0,0 +1,20 @@
+
\ No newline at end of file
diff --git a/app/assets/stylesheets/svg-dimensions.css b/app/assets/stylesheets/svg-dimensions.css
index 9f063d0e8..df97c6d2b 100644
--- a/app/assets/stylesheets/svg-dimensions.css
+++ b/app/assets/stylesheets/svg-dimensions.css
@@ -22,6 +22,14 @@
.icon-eyedropper { width: 17px; height: 17px; }
.icon-facebook-button { width: 29px; height: 24px; }
.icon-facebook { width: 17px; height: 17px; }
+.icon-file-archive { width: 24px; height: 31px; }
+.icon-file-code { width: 24px; height: 31px; }
+.icon-file-email { width: 24px; height: 31px; }
+.icon-file-excel { width: 24px; height: 31px; }
+.icon-file-pdf { width: 24px; height: 31px; }
+.icon-file-powerpoint { width: 24px; height: 31px; }
+.icon-file-unknown { width: 24px; height: 31px; }
+.icon-file-word { width: 24px; height: 31px; }
.icon-form { width: 17px; height: 17px; }
.icon-forward { width: 16px; height: 17px; }
.icon-full-logo { width: 175px; height: 50px; }
diff --git a/app/assets/stylesheets/zammad.scss b/app/assets/stylesheets/zammad.scss
index f06160715..09a6dc9e5 100644
--- a/app/assets/stylesheets/zammad.scss
+++ b/app/assets/stylesheets/zammad.scss
@@ -4957,17 +4957,34 @@ footer {
.attachments.attachments--list .attachments-title {
font-size: 13px;
color: hsl(60,1%,34%);
- font-weight: bold;
+ font-weight: 500;
+ text-transform: uppercase;
padding: 0 7px;
- margin: 0 0 4px;
}
.attachments .icon-paperclip {
position: absolute;
left: 33px;
top: 27px;
+ fill: hsl(240,1%,84%);
}
+ .attachments-block {
+ margin-bottom: 12px;
+
+ &:last-child {
+ margin-bottom: 0;
+ }
+
+ &-headline {
+ font-size: 13px;
+ color: hsl(60,1%,34%);
+ font-weight: 500;
+ text-transform: uppercase;
+ margin: 0 7px;
+ }
+ }
+
.ticket-article-item .task-subline {
margin-top: 12px;
}
@@ -5281,11 +5298,7 @@ footer {
border: none;
outline: none;
resize: none;
- }
-
- .articleNewEdit-body {
height: auto;
- min-height: 38px;
}
.article-new .bubble-arrow:after {
@@ -5312,28 +5325,53 @@ footer {
}
.attachment {
+ display: block;
font-size: 13px;
padding: 1px 10px 1px 7px;
- @include rtl(padding, 1px 7px 1px 10px);
- cursor: default;
position: relative;
- display: flex;
+ min-height: 42px;
+ color: inherit;
+ align-items: center;
+ border-bottom: 1px solid hsl(0,0%,96%);
+
+ &:last-child {
+ border-bottom: none;
+ }
}
- .attachment:hover {
- background: hsl(200,20%,97%);
- }
+ .attachment--preview {
+ padding: 9px 4px 9px 43px;
+ }
+
+ .attachment-icon {
+ position: absolute;
+ left: 0;
+ top: 9px;
+ width: 38px;
+ text-align: center;
+
+ svg {
+ vertical-align: bottom;
+ }
+
+ img {
+ width: 30px;
+ height: 30px;
+ object-fit: cover;
+ }
+ }
.attachment-name {
- @include bidi-style(margin-right, 5px, margin-left, 0);
min-width: 0;
+ display: block;
@extend .u-highlight;
+ word-break: break-all;
}
.attachment-size {
white-space: nowrap;
- float: right;
- @include bidi-style(margin-right, 10px, margin-left, 0);
+ font-size: 11px;
+ color: hsl(200,8%,77%);
}
.attachment-delete {
diff --git a/contrib/icon-sprite.sketch b/contrib/icon-sprite.sketch
index 751f95348..0e377212c 100644
Binary files a/contrib/icon-sprite.sketch and b/contrib/icon-sprite.sketch differ
diff --git a/db/migrate/20180220000001_setting_attachment_preview.rb b/db/migrate/20180220000001_setting_attachment_preview.rb
new file mode 100644
index 000000000..a3ab60ac1
--- /dev/null
+++ b/db/migrate/20180220000001_setting_attachment_preview.rb
@@ -0,0 +1,61 @@
+class SettingAttachmentPreview < ActiveRecord::Migration[5.1]
+ def up
+
+ # return if it's a new setup
+ return if !Setting.find_by(name: 'system_init_done')
+ Setting.create_if_not_exists(
+ title: 'Sidebar Attachments',
+ name: 'ui_ticket_zoom_attachments_preview',
+ area: 'UI::TicketZoom::Preview',
+ description: 'Enables preview of attachments.',
+ options: {
+ form: [
+ {
+ display: '',
+ null: true,
+ name: 'ui_ticket_zoom_attachments_preview',
+ tag: 'boolean',
+ translate: true,
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ },
+ ],
+ },
+ state: false,
+ preferences: {
+ prio: 400,
+ permission: ['admin.ui'],
+ },
+ frontend: true
+ )
+ Setting.create_if_not_exists(
+ title: 'Sidebar Attachments',
+ name: 'ui_ticket_zoom_sidebar_article_attachments',
+ area: 'UI::TicketZoom::Preview',
+ description: 'Enables a sidebar to show an overview of all attachments.',
+ options: {
+ form: [
+ {
+ display: '',
+ null: true,
+ name: 'ui_ticket_zoom_sidebar_article_attachments',
+ tag: 'boolean',
+ translate: true,
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ },
+ ],
+ },
+ state: false,
+ preferences: {
+ prio: 500,
+ permission: ['admin.ui'],
+ },
+ frontend: true
+ )
+ end
+end
diff --git a/db/seeds/settings.rb b/db/seeds/settings.rb
index 83ece67d1..e7d504abc 100644
--- a/db/seeds/settings.rb
+++ b/db/seeds/settings.rb
@@ -698,6 +698,61 @@ Setting.create_if_not_exists(
},
frontend: true
)
+Setting.create_if_not_exists(
+ title: 'Sidebar Attachments',
+ name: 'ui_ticket_zoom_attachments_preview',
+ area: 'UI::TicketZoom::Preview',
+ description: 'Enables preview of attachments.',
+ options: {
+ form: [
+ {
+ display: '',
+ null: true,
+ name: 'ui_ticket_zoom_attachments_preview',
+ tag: 'boolean',
+ translate: true,
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ },
+ ],
+ },
+ state: false,
+ preferences: {
+ prio: 400,
+ permission: ['admin.ui'],
+ },
+ frontend: true
+)
+Setting.create_if_not_exists(
+ title: 'Sidebar Attachments',
+ name: 'ui_ticket_zoom_sidebar_article_attachments',
+ area: 'UI::TicketZoom::Preview',
+ description: 'Enables a sidebar to show an overview of all attachments.',
+ options: {
+ form: [
+ {
+ display: '',
+ null: true,
+ name: 'ui_ticket_zoom_sidebar_article_attachments',
+ tag: 'boolean',
+ translate: true,
+ options: {
+ true => 'yes',
+ false => 'no',
+ },
+ },
+ ],
+ },
+ state: false,
+ preferences: {
+ prio: 500,
+ permission: ['admin.ui'],
+ },
+ frontend: true
+)
+
Setting.create_if_not_exists(
title: 'Set notes for ticket create types.',
name: 'ui_ticket_create_notes',
diff --git a/public/assets/images/icons.svg b/public/assets/images/icons.svg
index 5f9feffe3..987a49cb2 100644
--- a/public/assets/images/icons.svg
+++ b/public/assets/images/icons.svg
@@ -1,4 +1,6 @@
-