Merge branch 'develop' of git.znuny.com:zammad/zammad into develop
This commit is contained in:
commit
6aa189ee09
32 changed files with 514 additions and 120 deletions
|
@ -6,16 +6,20 @@ set -o errexit
|
||||||
set -o pipefail
|
set -o pipefail
|
||||||
|
|
||||||
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
REPO_ROOT="$(git rev-parse --show-toplevel)"
|
||||||
REPO_USER="zammad"
|
GITHUB_REPO_USER="zammad"
|
||||||
ZAMMAD_VERSION="$(git describe --tags | sed -e 's/-[a-z0-9]\{8,\}.*//g')"
|
ZAMMAD_VERSION="$(git describe --tags | sed -e 's/-[a-z0-9]\{8,\}.*//g')"
|
||||||
export ZAMMAD_VERSION
|
export ZAMMAD_VERSION
|
||||||
|
|
||||||
if [ "${CIRCLE_BRANCH}" == 'develop' ]; then
|
if [ "${CIRCLE_BRANCH}" == 'develop' ]; then
|
||||||
DOCKER_REPOSITORY="zammad-docker"
|
|
||||||
BUILD_SCRIPT="scripts/build_image.sh"
|
BUILD_SCRIPT="scripts/build_image.sh"
|
||||||
|
DOCKER_REPOSITORY="zammad"
|
||||||
|
export DOCKER_REPOSITORY
|
||||||
|
GITHUB_REPOSITORY="zammad-docker"
|
||||||
elif [ "${CIRCLE_BRANCH}" == 'stable' ]; then
|
elif [ "${CIRCLE_BRANCH}" == 'stable' ]; then
|
||||||
|
BUILD_SCRIPT="hooks/build"
|
||||||
DOCKER_REPOSITORY="zammad-docker-compose"
|
DOCKER_REPOSITORY="zammad-docker-compose"
|
||||||
BUILD_SCRIPT="hooks/build.sh"
|
export DOCKER_REPOSITORY
|
||||||
|
GITHUB_REPOSITORY="zammad-docker-compose"
|
||||||
else
|
else
|
||||||
echo "branch is ${CIRCLE_BRANCH}... no docker image build needed..."
|
echo "branch is ${CIRCLE_BRANCH}... no docker image build needed..."
|
||||||
exit 0
|
exit 0
|
||||||
|
@ -25,11 +29,11 @@ fi
|
||||||
echo "${DOCKER_PASSWORD}" | docker login --username="${DOCKER_USERNAME}" --password-stdin
|
echo "${DOCKER_PASSWORD}" | docker login --username="${DOCKER_USERNAME}" --password-stdin
|
||||||
|
|
||||||
# clone docker repo
|
# clone docker repo
|
||||||
git clone https://github.com/"${REPO_USER}"/"${DOCKER_REPOSITORY}"
|
git clone https://github.com/"${GITHUB_REPO_USER}"/"${GITHUB_REPOSITORY}"
|
||||||
|
|
||||||
# enter dockerfile dir
|
# enter dockerfile dir
|
||||||
cd "${REPO_ROOT}/${DOCKER_REPOSITORY}"
|
cd "${REPO_ROOT}/${GITHUB_REPOSITORY}"
|
||||||
|
|
||||||
# build & push docker image
|
# build & push docker image
|
||||||
# shellcheck disable=SC1090
|
# shellcheck disable=SC1090
|
||||||
source "${REPO_ROOT}/${DOCKER_REPOSITORY}/${BUILD_SCRIPT}"
|
source "${REPO_ROOT}/${GITHUB_REPOSITORY}/${BUILD_SCRIPT}"
|
||||||
|
|
|
@ -10,7 +10,7 @@ DB_CONFIG="test:\n adapter: postgresql\n database: zammad_test\n host: 127.0.
|
||||||
|
|
||||||
# install build dependencies
|
# install build dependencies
|
||||||
sudo apt-get update
|
sudo apt-get update
|
||||||
sudo apt-get install -y --no-install-recommends autoconf automake autotools-dev bison build-essential curl git-core libffi-dev libgdbm-dev libgmp-dev libmariadbclient-dev-compat libncurses5-dev libreadline-dev libsqlite3-dev libssl-dev libtool libxml2-dev libxslt1-dev libyaml-0-2 libyaml-dev patch pkg-config postfix sqlite3 zlib1g-dev
|
sudo apt-get install -y --no-install-recommends autoconf automake autotools-dev bison build-essential curl git-core libffi-dev libgdbm-dev libgmp-dev libmariadbclient-dev-compat libncurses5-dev libreadline-dev libsqlite3-dev libssl-dev libtool libxml2-dev libxslt1-dev libyaml-0-2 libyaml-dev patch pkg-config postfix sqlite3 zlib1g-dev libimlib2 libimlib2-dev
|
||||||
|
|
||||||
if [ "${CIRCLE_JOB}" == "install-mysql" ]; then
|
if [ "${CIRCLE_JOB}" == "install-mysql" ]; then
|
||||||
DB_ADAPTER="mysql2"
|
DB_ADAPTER="mysql2"
|
||||||
|
|
44
.pkgr.yml
44
.pkgr.yml
|
@ -10,36 +10,80 @@ targets:
|
||||||
- nginx
|
- nginx
|
||||||
- postgresql-server
|
- postgresql-server
|
||||||
- which
|
- which
|
||||||
|
- epel-release
|
||||||
|
- imlib2
|
||||||
|
- imlib2-devel
|
||||||
|
build_dependencies:
|
||||||
|
- http://download.fedoraproject.org/pub/epel/7/x86_64/Packages/i/imlib2-1.4.5-9.el7.x86_64.rpm
|
||||||
|
- http://download.fedoraproject.org/pub/epel/7/x86_64/Packages/i/imlib2-devel-1.4.5-9.el7.x86_64.rpm
|
||||||
debian-8:
|
debian-8:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- nginx|apache2
|
- nginx|apache2
|
||||||
- postgresql|mysql-server|mariadb-server|sqlite
|
- postgresql|mysql-server|mariadb-server|sqlite
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
|
build_dependencies:
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
debian-9:
|
debian-9:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- nginx|apache2
|
- nginx|apache2
|
||||||
- postgresql|mariadb-server|sqlite
|
- postgresql|mariadb-server|sqlite
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
|
build_dependencies:
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
ubuntu-16.04:
|
ubuntu-16.04:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- nginx|apache2
|
- nginx|apache2
|
||||||
- postgresql|mysql-server|mariadb-server|sqlite
|
- postgresql|mysql-server|mariadb-server|sqlite
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
|
build_dependencies:
|
||||||
|
- libimlib2
|
||||||
|
- libimlib2-dev
|
||||||
ubuntu-18.04:
|
ubuntu-18.04:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- nginx|apache2
|
- nginx|apache2
|
||||||
- postgresql|mysql-server|mariadb-server|sqlite
|
- postgresql|mysql-server|mariadb-server|sqlite
|
||||||
|
- libimlib2
|
||||||
|
build_dependencies:
|
||||||
|
- libimlib2-dev
|
||||||
sles-12:
|
sles-12:
|
||||||
dependencies:
|
dependencies:
|
||||||
- curl
|
- curl
|
||||||
- elasticsearch
|
- elasticsearch
|
||||||
- nginx
|
- nginx
|
||||||
- postgresql-server
|
- postgresql-server
|
||||||
|
- imlib2
|
||||||
|
- imlib2-devel
|
||||||
|
build_dependencies:
|
||||||
|
- imlib2
|
||||||
|
- imlib2-devel
|
||||||
|
sles-12:
|
||||||
|
dependencies:
|
||||||
|
- curl
|
||||||
|
- elasticsearch
|
||||||
|
- nginx
|
||||||
|
- postgresql-server
|
||||||
|
- imlib2
|
||||||
|
- libImlib2-1
|
||||||
|
- imlib2
|
||||||
|
- imlib2-devel
|
||||||
|
build_dependencies:
|
||||||
|
- https://ftp.gwdg.de/pub/opensuse/discontinued/distribution/12.3/repo/oss/suse/x86_64/imlib2-1.4.5-12.1.1.x86_64.rpm
|
||||||
|
- https://ftp.gwdg.de/pub/opensuse/discontinued/distribution/12.3/repo/oss/suse/x86_64/imlib2-devel-1.4.5-12.1.1.x86_64.rpm
|
||||||
|
- https://ftp.gwdg.de/pub/opensuse/discontinued/distribution/12.3/repo/oss/suse/x86_64/imlib2-filters-1.4.5-12.1.1.x86_64.rpm
|
||||||
|
- https://ftp.gwdg.de/pub/opensuse/discontinued/distribution/12.3/repo/oss/suse/x86_64/libImlib2-1-1.4.5-12.1.1.x86_64.rpm
|
||||||
before:
|
before:
|
||||||
- contrib/packager.io/before.sh
|
- contrib/packager.io/before.sh
|
||||||
after:
|
after:
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
## [2.9.0](https://github.com/zammad/zammad/tree/2.9.0) (2018-xx-xx)
|
## [2.10.0](https://github.com/zammad/zammad/tree/2.10.0) (2019-xx-xx)
|
||||||
[Full Changelog](https://github.com/zammad/zammad/compare/2.8.0...2.9.0)
|
[Full Changelog](https://github.com/zammad/zammad/compare/2.8.0...2.10.0)
|
||||||
|
|
||||||
**Implemented enhancements:**
|
**Implemented enhancements:**
|
||||||
|
|
||||||
|
|
3
Gemfile
3
Gemfile
|
@ -116,6 +116,9 @@ gem 'autodiscover', git: 'https://github.com/zammad-deps/autodiscover'
|
||||||
gem 'rubyntlm', git: 'https://github.com/wimm/rubyntlm'
|
gem 'rubyntlm', git: 'https://github.com/wimm/rubyntlm'
|
||||||
gem 'viewpoint'
|
gem 'viewpoint'
|
||||||
|
|
||||||
|
# image processing
|
||||||
|
gem 'rszr'
|
||||||
|
|
||||||
# Gems used only for develop/test and not required
|
# Gems used only for develop/test and not required
|
||||||
# in production environments by default.
|
# in production environments by default.
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
|
|
|
@ -408,6 +408,7 @@ GEM
|
||||||
rspec-mocks (~> 3.8.0)
|
rspec-mocks (~> 3.8.0)
|
||||||
rspec-support (~> 3.8.0)
|
rspec-support (~> 3.8.0)
|
||||||
rspec-support (3.8.0)
|
rspec-support (3.8.0)
|
||||||
|
rszr (0.3.2)
|
||||||
rubocop (0.64.0)
|
rubocop (0.64.0)
|
||||||
jaro_winkler (~> 1.5.1)
|
jaro_winkler (~> 1.5.1)
|
||||||
parallel (~> 1.10)
|
parallel (~> 1.10)
|
||||||
|
@ -584,6 +585,7 @@ DEPENDENCIES
|
||||||
rb-fsevent
|
rb-fsevent
|
||||||
rchardet (>= 1.8.0)
|
rchardet (>= 1.8.0)
|
||||||
rspec-rails
|
rspec-rails
|
||||||
|
rszr
|
||||||
rubocop
|
rubocop
|
||||||
rubyntlm!
|
rubyntlm!
|
||||||
sassc-rails
|
sassc-rails
|
||||||
|
|
2
VERSION
2
VERSION
|
@ -1 +1 @@
|
||||||
2.9.x
|
2.10.x
|
||||||
|
|
|
@ -808,7 +808,8 @@ class App.TicketZoom extends App.Controller
|
||||||
@autosaveStop()
|
@autosaveStop()
|
||||||
|
|
||||||
# validate ticket form using HTML5 validity check
|
# validate ticket form using HTML5 validity check
|
||||||
if !@$('.edit').parent().get(0).reportValidity()
|
element = @$('.edit').parent().get(0)
|
||||||
|
if element && element.reportValidity && !element.reportValidity()
|
||||||
@submitEnable(e)
|
@submitEnable(e)
|
||||||
@autosaveStart()
|
@autosaveStart()
|
||||||
return
|
return
|
||||||
|
|
|
@ -11,9 +11,33 @@ class App.TicketZoomArticleImageView extends App.ControllerModal
|
||||||
'click .js-cancel': 'cancel'
|
'click .js-cancel': 'cancel'
|
||||||
'click .js-close': 'cancel'
|
'click .js-close': 'cancel'
|
||||||
|
|
||||||
|
constructor: ->
|
||||||
|
super
|
||||||
|
@unbindAll()
|
||||||
|
$(document).bind('keydown.image_preview', 'right', (e) =>
|
||||||
|
nextElement = @parentElement.closest('.attachment').next('.attachment.attachment--preview')
|
||||||
|
return if nextElement.length is 0
|
||||||
|
@close()
|
||||||
|
nextElement.find('img').click()
|
||||||
|
)
|
||||||
|
$(document).bind('keydown.image_preview', 'left', (e) =>
|
||||||
|
prevElement = @parentElement.closest('.attachment').prev('.attachment.attachment--preview')
|
||||||
|
return if prevElement.length is 0
|
||||||
|
@close()
|
||||||
|
prevElement.find('img').click()
|
||||||
|
)
|
||||||
|
|
||||||
content: ->
|
content: ->
|
||||||
|
@image = @image.replace(/view=preview/, 'view=inline')
|
||||||
"<div class=\"centered imagePreview\">#{@image}</div>"
|
"<div class=\"centered imagePreview\">#{@image}</div>"
|
||||||
|
|
||||||
onSubmit: =>
|
onSubmit: =>
|
||||||
|
@image = @image.replace(/(\?|)view=(preview|inline)/, '')
|
||||||
url = "#{$(@image).attr('src')}?disposition=attachment"
|
url = "#{$(@image).attr('src')}?disposition=attachment"
|
||||||
window.open(url, '_blank')
|
window.open(url, '_blank')
|
||||||
|
|
||||||
|
onClose: =>
|
||||||
|
@unbindAll()
|
||||||
|
|
||||||
|
unbindAll: ->
|
||||||
|
$(document).unbind('keydown.image_preview')
|
||||||
|
|
|
@ -421,4 +421,4 @@ class ArticleViewItem extends App.ObserverController
|
||||||
imageView: (e) ->
|
imageView: (e) ->
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
new App.TicketZoomArticleImageView(image: $(e.target).get(0).outerHTML)
|
new App.TicketZoomArticleImageView(image: $(e.target).get(0).outerHTML, parentElement: $(e.currentTarget))
|
||||||
|
|
|
@ -66,7 +66,7 @@
|
||||||
<div class="attachment-icon">
|
<div class="attachment-icon">
|
||||||
<% if attachment.preferences && attachment.preferences['Content-Type'] && @ContentTypeIcon(attachment.preferences['Content-Type']): %>
|
<% if attachment.preferences && attachment.preferences['Content-Type'] && @ContentTypeIcon(attachment.preferences['Content-Type']): %>
|
||||||
<% if @canPreview(attachment.preferences['Content-Type']): %>
|
<% if @canPreview(attachment.preferences['Content-Type']): %>
|
||||||
<img src="<%= App.Config.get('api_path') %>/ticket_attachment/<%= @article.ticket_id %>/<%= @article.id %>/<%= attachment.id %>">
|
<img src="<%= App.Config.get('api_path') %>/ticket_attachment/<%= @article.ticket_id %>/<%= @article.id %>/<%= attachment.id %>?view=preview">
|
||||||
<% else: %>
|
<% else: %>
|
||||||
<%- @Icon( @ContentTypeIcon(attachment.preferences['Content-Type']) ) %>
|
<%- @Icon( @ContentTypeIcon(attachment.preferences['Content-Type']) ) %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
class SessionsController < ApplicationController
|
class SessionsController < ApplicationController
|
||||||
prepend_before_action :authentication_check, only: %i[switch_to_user list delete]
|
prepend_before_action :authentication_check, only: %i[switch_to_user list delete]
|
||||||
skip_before_action :verify_csrf_token, only: %i[create show destroy create_omniauth failure_omniauth create_sso]
|
skip_before_action :verify_csrf_token, only: %i[show destroy create_omniauth failure_omniauth create_sso]
|
||||||
|
|
||||||
# "Create" a login, aka "log the user in"
|
# "Create" a login, aka "log the user in"
|
||||||
def create
|
def create
|
||||||
|
|
|
@ -255,8 +255,21 @@ class TicketArticlesController < ApplicationController
|
||||||
|
|
||||||
disposition = sanitized_disposition
|
disposition = sanitized_disposition
|
||||||
|
|
||||||
|
content = nil
|
||||||
|
if params[:view].present? && file.preferences[:resizable] == true
|
||||||
|
if file.preferences[:content_inline] == true && params[:view] == 'inline'
|
||||||
|
content = file.content_inline
|
||||||
|
elsif file.preferences[:content_preview] == true && params[:view] == 'preview'
|
||||||
|
content = file.content_preview
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if content.blank?
|
||||||
|
content = file.content
|
||||||
|
end
|
||||||
|
|
||||||
send_data(
|
send_data(
|
||||||
file.content,
|
content,
|
||||||
filename: file.filename,
|
filename: file.filename,
|
||||||
type: file.preferences['Content-Type'] || file.preferences['Mime-Type'] || 'application/octet-stream',
|
type: file.preferences['Content-Type'] || file.preferences['Mime-Type'] || 'application/octet-stream',
|
||||||
disposition: disposition
|
disposition: disposition
|
||||||
|
|
|
@ -7,6 +7,7 @@ class Organization < ApplicationModel
|
||||||
include HasHistory
|
include HasHistory
|
||||||
include HasSearchIndexBackend
|
include HasSearchIndexBackend
|
||||||
include CanCsvImport
|
include CanCsvImport
|
||||||
|
include ChecksHtmlSanitized
|
||||||
|
|
||||||
include Organization::ChecksAccess
|
include Organization::ChecksAccess
|
||||||
include Organization::Assets
|
include Organization::Assets
|
||||||
|
@ -22,6 +23,8 @@ class Organization < ApplicationModel
|
||||||
|
|
||||||
activity_stream_permission 'admin.role'
|
activity_stream_permission 'admin.role'
|
||||||
|
|
||||||
|
sanitized_html :note
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def domain_cleanup
|
def domain_cleanup
|
||||||
|
|
|
@ -34,7 +34,7 @@ returns
|
||||||
=end
|
=end
|
||||||
|
|
||||||
def self.add(data)
|
def self.add(data)
|
||||||
data = data.stringify_keys
|
data.deep_stringify_keys!
|
||||||
|
|
||||||
# lookup store_object.id
|
# lookup store_object.id
|
||||||
store_object = Store::Object.create_if_not_exists(name: data['object'])
|
store_object = Store::Object.create_if_not_exists(name: data['object'])
|
||||||
|
@ -50,9 +50,34 @@ returns
|
||||||
data.delete('data')
|
data.delete('data')
|
||||||
data.delete('object')
|
data.delete('object')
|
||||||
|
|
||||||
|
data['preferences'] ||= {}
|
||||||
|
['Mime-Type', 'Content-Type', 'mime_type', 'content_type'].each do |key|
|
||||||
|
next if data['preferences'][key].blank?
|
||||||
|
next if !data['preferences'][key].match(%r{image/(jpeg|jpg|png)}i)
|
||||||
|
|
||||||
|
data['preferences']['resizable'] = true
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
# store meta data
|
# store meta data
|
||||||
store = Store.create!(data)
|
store = Store.create!(data)
|
||||||
|
|
||||||
|
begin
|
||||||
|
if store.preferences[:resizable] == true
|
||||||
|
if store.content_preview(silence: true)
|
||||||
|
store.preferences[:content_preview] = true
|
||||||
|
end
|
||||||
|
if store.content_inline(silence: true)
|
||||||
|
store.preferences[:content_inline] = true
|
||||||
|
end
|
||||||
|
store.save!
|
||||||
|
end
|
||||||
|
rescue => e
|
||||||
|
logger.error e
|
||||||
|
store.preferences[:resizable] = false
|
||||||
|
store.save!
|
||||||
|
end
|
||||||
|
|
||||||
store
|
store
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -165,6 +190,52 @@ returns
|
||||||
|
|
||||||
=begin
|
=begin
|
||||||
|
|
||||||
|
get content of file in preview size
|
||||||
|
|
||||||
|
store = Store.find(store_id)
|
||||||
|
content_as_string = store.content_preview
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
content_as_string
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def content_preview(options = {})
|
||||||
|
file = Store::File.find_by(id: store_file_id)
|
||||||
|
if !file
|
||||||
|
raise "No such file #{store_file_id}!"
|
||||||
|
end
|
||||||
|
raise 'Unable to generate preview' if options[:silence] != true && preferences[:content_preview] != true
|
||||||
|
|
||||||
|
image_resize(file.content, 200)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
|
get content of file in inline size
|
||||||
|
|
||||||
|
store = Store.find(store_id)
|
||||||
|
content_as_string = store.content_inline
|
||||||
|
|
||||||
|
returns
|
||||||
|
|
||||||
|
content_as_string
|
||||||
|
|
||||||
|
=end
|
||||||
|
|
||||||
|
def content_inline(options = {})
|
||||||
|
file = Store::File.find_by(id: store_file_id)
|
||||||
|
if !file
|
||||||
|
raise "No such file #{store_file_id}!"
|
||||||
|
end
|
||||||
|
raise 'Unable to generate inline' if options[:silence] != true && preferences[:content_inline] != true
|
||||||
|
|
||||||
|
image_resize(file.content, 1800)
|
||||||
|
end
|
||||||
|
|
||||||
|
=begin
|
||||||
|
|
||||||
get content of file
|
get content of file
|
||||||
|
|
||||||
store = Store.find(store_id)
|
store = Store.find(store_id)
|
||||||
|
@ -204,4 +275,32 @@ returns
|
||||||
|
|
||||||
file.provider
|
file.provider
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def image_resize(content, width)
|
||||||
|
local_sha = Digest::SHA256.hexdigest(content)
|
||||||
|
|
||||||
|
cache_key = "image-resize-#{local_sha}_#{width}"
|
||||||
|
all = nil
|
||||||
|
image = Cache.get(cache_key)
|
||||||
|
return image if image
|
||||||
|
|
||||||
|
temp_file = ::Tempfile.new
|
||||||
|
temp_file.binmode
|
||||||
|
temp_file.write(content)
|
||||||
|
temp_file.close
|
||||||
|
image = Rszr::Image.load(temp_file.path)
|
||||||
|
return if image.width < width
|
||||||
|
|
||||||
|
image.resize!(width, :auto)
|
||||||
|
temp_file_resize = ::Tempfile.new.path
|
||||||
|
image.save(temp_file_resize)
|
||||||
|
image_resized = ::File.binread(temp_file_resize)
|
||||||
|
|
||||||
|
Cache.write(cache_key, image_resized, { expires_in: 6.months })
|
||||||
|
|
||||||
|
image_resized
|
||||||
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class Ticket < ApplicationModel
|
||||||
include ChecksClientNotification
|
include ChecksClientNotification
|
||||||
include ChecksLatestChangeObserved
|
include ChecksLatestChangeObserved
|
||||||
include CanCsvImport
|
include CanCsvImport
|
||||||
|
include ChecksHtmlSanitized
|
||||||
include HasHistory
|
include HasHistory
|
||||||
include HasTags
|
include HasTags
|
||||||
include HasSearchIndexBackend
|
include HasSearchIndexBackend
|
||||||
|
@ -56,6 +57,8 @@ class Ticket < ApplicationModel
|
||||||
|
|
||||||
history_relation_object 'Ticket::Article'
|
history_relation_object 'Ticket::Article'
|
||||||
|
|
||||||
|
sanitized_html :note
|
||||||
|
|
||||||
belongs_to :group
|
belongs_to :group
|
||||||
belongs_to :organization
|
belongs_to :organization
|
||||||
has_many :articles, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update, dependent: :destroy, inverse_of: :ticket
|
has_many :articles, class_name: 'Ticket::Article', after_add: :cache_update, after_remove: :cache_update, dependent: :destroy, inverse_of: :ticket
|
||||||
|
|
|
@ -82,7 +82,7 @@ returns
|
||||||
article['attachments'].each do |file|
|
article['attachments'].each do |file|
|
||||||
next if !file[:preferences] || !file[:preferences]['Content-ID'] || (file[:preferences]['Content-ID'] != cid && file[:preferences]['Content-ID'] != "<#{cid}>" )
|
next if !file[:preferences] || !file[:preferences]['Content-ID'] || (file[:preferences]['Content-ID'] != cid && file[:preferences]['Content-ID'] != "<#{cid}>" )
|
||||||
|
|
||||||
replace = "#{tag_start}/api/v1/ticket_attachment/#{article['ticket_id']}/#{article['id']}/#{file[:id]}\"#{tag_end}>"
|
replace = "#{tag_start}/api/v1/ticket_attachment/#{article['ticket_id']}/#{article['id']}/#{file[:id]}?view=inline\"#{tag_end}>"
|
||||||
inline_attachments[file[:id]] = true
|
inline_attachments[file[:id]] = true
|
||||||
break
|
break
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,6 +6,7 @@ class User < ApplicationModel
|
||||||
include HasHistory
|
include HasHistory
|
||||||
include HasSearchIndexBackend
|
include HasSearchIndexBackend
|
||||||
include CanCsvImport
|
include CanCsvImport
|
||||||
|
include ChecksHtmlSanitized
|
||||||
include HasGroups
|
include HasGroups
|
||||||
include HasRoles
|
include HasRoles
|
||||||
|
|
||||||
|
@ -66,6 +67,8 @@ class User < ApplicationModel
|
||||||
:groups,
|
:groups,
|
||||||
:user_groups
|
:user_groups
|
||||||
|
|
||||||
|
sanitized_html :note
|
||||||
|
|
||||||
def ignore_search_indexing?(_action)
|
def ignore_search_indexing?(_action)
|
||||||
# ignore internal user
|
# ignore internal user
|
||||||
return true if id == 1
|
return true if id == 1
|
||||||
|
|
|
@ -0,0 +1,34 @@
|
||||||
|
class SettingChangeTicketZoomAttachmentPreview < ActiveRecord::Migration[5.1]
|
||||||
|
def up
|
||||||
|
# return if it's a new setup
|
||||||
|
return if !Setting.find_by(name: 'system_init_done')
|
||||||
|
|
||||||
|
Setting.create_or_update(
|
||||||
|
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: true,
|
||||||
|
preferences: {
|
||||||
|
prio: 400,
|
||||||
|
permission: ['admin.ui'],
|
||||||
|
},
|
||||||
|
frontend: true
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
|
@ -793,7 +793,7 @@ Setting.create_if_not_exists(
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
state: false,
|
state: true,
|
||||||
preferences: {
|
preferences: {
|
||||||
prio: 400,
|
prio: 400,
|
||||||
permission: ['admin.ui'],
|
permission: ['admin.ui'],
|
||||||
|
|
|
@ -17,35 +17,31 @@ examples how to use
|
||||||
end
|
end
|
||||||
|
|
||||||
def to_s
|
def to_s
|
||||||
strip_html
|
@template.gsub(/\#{\s*(.*?)\s*}/m) do
|
||||||
end
|
# some browsers start adding HTML tags
|
||||||
|
# fixes https://github.com/zammad/zammad/issues/385
|
||||||
|
input_template = $1.gsub(/\A<.+?>\s*|\s*<.+?>\z/, '')
|
||||||
|
|
||||||
def strip_html
|
case input_template
|
||||||
# some browsers start adding HTML tags
|
when /\At\('(.+?)'\)\z/m
|
||||||
# fixes https://github.com/zammad/zammad/issues/385
|
%(<%= t "#{sanitize_text($1)}", #{@escape} %>)
|
||||||
@template.gsub(/\#\{\s*t\((.+?)\)\s*\}/m) do
|
when /\At\((.+?)\)\z/m
|
||||||
content = $1
|
%(<%= t d"#{sanitize_object_name($1)}", #{@escape} %>)
|
||||||
if content =~ /^'(.+?)'$/
|
when /\Aconfig\.(.+?)\z/m
|
||||||
%(<%= t "#{strip_content($1)}", #{@escape} %>)
|
%(<%= c "#{sanitize_object_name($1)}", #{@escape} %>)
|
||||||
else
|
else
|
||||||
%(<%= t d"#{strip_variable(content)}", #{@escape} %>)
|
%(<%= d "#{sanitize_object_name(input_template)}", #{@escape} %>)
|
||||||
end
|
end
|
||||||
end.gsub(/\#\{\s*config\.(.+?)\s*\}/m) do
|
|
||||||
%(<%= c "#{strip_variable($1)}", #{@escape} %>)
|
|
||||||
end.gsub(/\#\{(.*?)\}/m) do
|
|
||||||
%(<%= d "#{strip_variable($1)}", #{@escape} %>)
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
def strip_content(string)
|
def sanitize_text(string)
|
||||||
string&.gsub(/\t|\r|\n/, '')
|
string&.tr("\t\r\n", '')
|
||||||
&.gsub(/"/, '\"')
|
&.gsub(/(?<!\\)(?=")/, '\\')
|
||||||
end
|
end
|
||||||
|
|
||||||
def strip_variable(string)
|
def sanitize_object_name(string)
|
||||||
string&.gsub(/\t|\r|\n|"|'|§|;/, '')
|
string&.tr("\t\r\n\f \"'§;", '')
|
||||||
&.gsub(/\s*/, '')
|
|
||||||
&.gsub(/<.+?>/, '')
|
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -501,7 +501,7 @@ do(window) ->
|
||||||
stopPropagation: (event) ->
|
stopPropagation: (event) ->
|
||||||
event.stopPropagation()
|
event.stopPropagation()
|
||||||
|
|
||||||
onPaste: (e) =>
|
onDrop: (e) =>
|
||||||
e.stopPropagation()
|
e.stopPropagation()
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
|
|
||||||
|
|
91
spec/lib/notification_factory/template_spec.rb
Normal file
91
spec/lib/notification_factory/template_spec.rb
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe NotificationFactory::Template do
|
||||||
|
subject(:template) do
|
||||||
|
NotificationFactory::Template.new(template_string, escape)
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_s' do
|
||||||
|
context 'for empty input template (incl. whitespace-only)' do
|
||||||
|
let(:template_string) { "\#{ }" }
|
||||||
|
|
||||||
|
context 'with escape = true' do
|
||||||
|
let(:escape) { true }
|
||||||
|
|
||||||
|
it 'returns an ERB template with the #d helper, and passes escape arg as string' do
|
||||||
|
expect(template.to_s).to eq('<%= d "", true %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with escape = false' do
|
||||||
|
let(:escape) { false }
|
||||||
|
|
||||||
|
it 'returns an ERB template with the #d helper, and passes escape arg as string' do
|
||||||
|
expect(template.to_s).to eq('<%= d "", false %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for input template using #t helper' do
|
||||||
|
let(:template_string) { "\#{t('some text')}" }
|
||||||
|
let(:escape) { false }
|
||||||
|
|
||||||
|
it 'returns an ERB template with the #t helper, and passes escape arg as string' do
|
||||||
|
expect(template.to_s).to eq('<%= t "some text", false %>')
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with double-quotes in argument' do
|
||||||
|
let(:template_string) { "\#{t('some \"text\"')}" }
|
||||||
|
|
||||||
|
it 'adds backslash-escaping' do
|
||||||
|
expect(template.to_s).to eq('<%= t "some \"text\"", false %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Regression test for https://github.com/zammad/zammad/issues/385
|
||||||
|
context 'with HTML auto-injected by browser' do
|
||||||
|
let(:escape) { true }
|
||||||
|
|
||||||
|
context 'for <a> tags wrapped around "ticket.id"' do
|
||||||
|
let(:template_string) { <<~'TEMPLATE'.chomp }
|
||||||
|
#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id</a>}
|
||||||
|
TEMPLATE
|
||||||
|
|
||||||
|
it 'strips tag from resulting ERB template' do
|
||||||
|
expect(template.to_s).to eq('<%= d "ticket.id", true %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for <a> tags wrapped around "config.fqdn"' do
|
||||||
|
let(:template_string) { <<~'TEMPLATE'.chomp }
|
||||||
|
#{<a href="http://config.fqdn" title="http://config.fqdn" target="_blank">config.fqdn</a>}
|
||||||
|
TEMPLATE
|
||||||
|
|
||||||
|
it 'strips tag from resulting ERB template' do
|
||||||
|
expect(template.to_s).to eq('<%= c "fqdn", true %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for <a> tags surrounded by whitespace' do
|
||||||
|
let(:template_string) { <<~'TEMPLATE'.chomp }
|
||||||
|
#{ <a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id </a> }
|
||||||
|
TEMPLATE
|
||||||
|
|
||||||
|
it 'strips tag and spaces from template' do
|
||||||
|
expect(template.to_s).to eq('<%= d "ticket.id", true %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'for unpaired <a> tag and trailing whitespace' do
|
||||||
|
let(:template_string) { <<~'TEMPLATE'.chomp }
|
||||||
|
#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id }
|
||||||
|
TEMPLATE
|
||||||
|
|
||||||
|
it 'strips tag and spaces from template' do
|
||||||
|
expect(template.to_s).to eq('<%= d "ticket.id", true %>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
10
spec/models/concerns/has_xss_sanitized_note_examples.rb
Normal file
10
spec/models/concerns/has_xss_sanitized_note_examples.rb
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
RSpec.shared_examples 'HasXssSanitizedNote' do |model_factory:|
|
||||||
|
describe 'XSS prevention' do
|
||||||
|
context 'with injected JS' do
|
||||||
|
subject { create(model_factory, note: 'test 123 <script type="text/javascript">alert("XSS!");</script> <b>some text</b>') }
|
||||||
|
it 'strips out <script> tag' do
|
||||||
|
expect(subject.note).to eq('test 123 alert("XSS!"); <b>some text</b>')
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,10 +1,12 @@
|
||||||
require 'rails_helper'
|
require 'rails_helper'
|
||||||
require 'models/concerns/can_lookup_examples'
|
require 'models/concerns/can_lookup_examples'
|
||||||
require 'models/concerns/has_search_index_backend_examples'
|
require 'models/concerns/has_search_index_backend_examples'
|
||||||
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
|
|
||||||
RSpec.describe Organization, type: :model do
|
RSpec.describe Organization, type: :model do
|
||||||
it_behaves_like 'CanLookup'
|
it_behaves_like 'CanLookup'
|
||||||
it_behaves_like 'HasSearchIndexBackend', indexed_factory: :organization
|
it_behaves_like 'HasSearchIndexBackend', indexed_factory: :organization
|
||||||
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :organization
|
||||||
|
|
||||||
describe '.where_or_cis' do
|
describe '.where_or_cis' do
|
||||||
it 'finds instance by querying multiple attributes case insensitive' do
|
it 'finds instance by querying multiple attributes case insensitive' do
|
||||||
|
@ -13,4 +15,5 @@ RSpec.describe Organization, type: :model do
|
||||||
expect(organizations).not_to be_blank
|
expect(organizations).not_to be_blank
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,11 +2,13 @@ require 'rails_helper'
|
||||||
require 'models/application_model_examples'
|
require 'models/application_model_examples'
|
||||||
require 'models/concerns/can_be_imported_examples'
|
require 'models/concerns/can_be_imported_examples'
|
||||||
require 'models/concerns/can_lookup_examples'
|
require 'models/concerns/can_lookup_examples'
|
||||||
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
|
|
||||||
RSpec.describe Ticket, type: :model do
|
RSpec.describe Ticket, type: :model do
|
||||||
it_behaves_like 'ApplicationModel'
|
it_behaves_like 'ApplicationModel'
|
||||||
it_behaves_like 'CanBeImported'
|
it_behaves_like 'CanBeImported'
|
||||||
it_behaves_like 'CanLookup'
|
it_behaves_like 'CanLookup'
|
||||||
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :ticket
|
||||||
|
|
||||||
subject(:ticket) { create(:ticket) }
|
subject(:ticket) { create(:ticket) }
|
||||||
|
|
||||||
|
@ -378,4 +380,5 @@ RSpec.describe Ticket, type: :model do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,7 @@ require 'models/application_model_examples'
|
||||||
require 'models/concerns/has_groups_examples'
|
require 'models/concerns/has_groups_examples'
|
||||||
require 'models/concerns/has_roles_examples'
|
require 'models/concerns/has_roles_examples'
|
||||||
require 'models/concerns/has_groups_permissions_examples'
|
require 'models/concerns/has_groups_permissions_examples'
|
||||||
|
require 'models/concerns/has_xss_sanitized_note_examples'
|
||||||
require 'models/concerns/can_be_imported_examples'
|
require 'models/concerns/can_be_imported_examples'
|
||||||
require 'models/concerns/can_lookup_examples'
|
require 'models/concerns/can_lookup_examples'
|
||||||
|
|
||||||
|
@ -10,6 +11,7 @@ RSpec.describe User do
|
||||||
it_behaves_like 'ApplicationModel'
|
it_behaves_like 'ApplicationModel'
|
||||||
it_behaves_like 'HasGroups', group_access_factory: :agent_user
|
it_behaves_like 'HasGroups', group_access_factory: :agent_user
|
||||||
it_behaves_like 'HasRoles', group_access_factory: :agent_user
|
it_behaves_like 'HasRoles', group_access_factory: :agent_user
|
||||||
|
it_behaves_like 'HasXssSanitizedNote', model_factory: :user
|
||||||
it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
|
it_behaves_like 'HasGroups and Permissions', group_access_no_permission_factory: :user
|
||||||
it_behaves_like 'CanBeImported'
|
it_behaves_like 'CanBeImported'
|
||||||
it_behaves_like 'CanLookup'
|
it_behaves_like 'CanLookup'
|
||||||
|
@ -831,4 +833,5 @@ RSpec.describe User do
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,6 +2,17 @@ require 'rails_helper'
|
||||||
|
|
||||||
RSpec.describe 'Api Auth', type: :request do
|
RSpec.describe 'Api Auth', type: :request do
|
||||||
|
|
||||||
|
around(:each) do |example|
|
||||||
|
orig = ActionController::Base.allow_forgery_protection
|
||||||
|
|
||||||
|
begin
|
||||||
|
ActionController::Base.allow_forgery_protection = true
|
||||||
|
example.run
|
||||||
|
ensure
|
||||||
|
ActionController::Base.allow_forgery_protection = orig
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
let(:admin_user) do
|
let(:admin_user) do
|
||||||
create(:admin_user)
|
create(:admin_user)
|
||||||
end
|
end
|
||||||
|
@ -369,7 +380,10 @@ RSpec.describe 'Api Auth', type: :request do
|
||||||
it 'does session auth - admin' do
|
it 'does session auth - admin' do
|
||||||
create(:admin_user, login: 'api-admin@example.com', password: 'adminpw')
|
create(:admin_user, login: 'api-admin@example.com', password: 'adminpw')
|
||||||
|
|
||||||
post '/api/v1/signin', params: { username: 'api-admin@example.com', password: 'adminpw', fingerprint: '123456789' }
|
get '/'
|
||||||
|
token = response.headers['CSRF-TOKEN']
|
||||||
|
|
||||||
|
post '/api/v1/signin', params: { username: 'api-admin@example.com', password: 'adminpw', fingerprint: '123456789' }, headers: { 'X-CSRF-Token' => token }
|
||||||
expect(response.header.key?('Access-Control-Allow-Origin')).to be_falsey
|
expect(response.header.key?('Access-Control-Allow-Origin')).to be_falsey
|
||||||
expect(response).to have_http_status(201)
|
expect(response).to have_http_status(201)
|
||||||
|
|
||||||
|
|
BIN
test/data/image/1000x1000.png
Normal file
BIN
test/data/image/1000x1000.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.4 KiB |
BIN
test/data/image/1x1.png
Normal file
BIN
test/data/image/1x1.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 106 B |
|
@ -1,79 +0,0 @@
|
||||||
require 'test_helper'
|
|
||||||
|
|
||||||
class NotificationFactoryTemplateTest < ActiveSupport::TestCase
|
|
||||||
|
|
||||||
# RSpec incoming!
|
|
||||||
def described_class
|
|
||||||
NotificationFactory::Template
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'regular browser html' do
|
|
||||||
|
|
||||||
# ensures https://github.com/zammad/zammad/issues/385
|
|
||||||
template_before = '#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id</a>}'
|
|
||||||
template_after = '<%= d "ticket.id", true %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, true).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
|
|
||||||
template_before = '#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">config.fqdn</a>}'
|
|
||||||
template_after = '<%= d "config.fqdn", true %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, true).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'spaced browser html' do
|
|
||||||
|
|
||||||
# ensures https://github.com/zammad/zammad/issues/385
|
|
||||||
template_before = '#{ <a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id </a> }'
|
|
||||||
template_after = '<%= d "ticket.id", true %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, true).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'broken browser html' do
|
|
||||||
|
|
||||||
# ensures https://github.com/zammad/zammad/issues/385
|
|
||||||
template_before = '#{<a href="http://ticket.id" title="http://ticket.id" target="_blank">ticket.id }'
|
|
||||||
template_after = '<%= d "ticket.id", true %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, true).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'empty tag' do
|
|
||||||
|
|
||||||
template_before = '#{}'
|
|
||||||
template_after = '<%= d "", true %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, true).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'empty tag with space' do
|
|
||||||
|
|
||||||
template_before = '#{ }'
|
|
||||||
template_after = '<%= d "", false %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, false).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
test 'translation' do
|
|
||||||
|
|
||||||
template_before = "\#{t('some text')}"
|
|
||||||
template_after = '<%= t "some text", false %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, false).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
|
|
||||||
template_before = "\#{t('some \"text\"')}"
|
|
||||||
template_after = '<%= t "some \"text\"", false %>'
|
|
||||||
|
|
||||||
result = described_class.new(template_before, false).to_s
|
|
||||||
assert_equal(template_after, result)
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
|
@ -151,4 +151,124 @@ class StoreTest < ActiveSupport::TestCase
|
||||||
assert_not(attachments[0])
|
assert_not(attachments[0])
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
test 'test resizable' do
|
||||||
|
|
||||||
|
# not possible
|
||||||
|
store = Store.add(
|
||||||
|
object: 'SomeObject1',
|
||||||
|
o_id: rand(1_234_567_890),
|
||||||
|
data: File.binread(Rails.root.join('test', 'data', 'upload', 'upload1.txt')),
|
||||||
|
filename: 'test1.pdf',
|
||||||
|
preferences: {
|
||||||
|
content_type: 'text/plain',
|
||||||
|
content_id: 234,
|
||||||
|
},
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_not(store.preferences.key?(:resizable))
|
||||||
|
assert_not(store.preferences.key?(:content_inline))
|
||||||
|
assert_not(store.preferences.key?(:content_preview))
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_inline
|
||||||
|
end
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_preview
|
||||||
|
end
|
||||||
|
|
||||||
|
# not possible
|
||||||
|
store = Store.add(
|
||||||
|
object: 'SomeObject2',
|
||||||
|
o_id: rand(1_234_567_890),
|
||||||
|
data: File.binread(Rails.root.join('test', 'data', 'upload', 'upload1.txt')),
|
||||||
|
filename: 'test1.pdf',
|
||||||
|
preferences: {
|
||||||
|
content_type: 'image/jpg',
|
||||||
|
content_id: 234,
|
||||||
|
},
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(store.preferences[:resizable], false)
|
||||||
|
assert_not(store.preferences.key?(:content_inline))
|
||||||
|
assert_not(store.preferences.key?(:content_preview))
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_inline
|
||||||
|
end
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_preview
|
||||||
|
end
|
||||||
|
|
||||||
|
# possible (preview and inline)
|
||||||
|
store = Store.add(
|
||||||
|
object: 'SomeObject3',
|
||||||
|
o_id: rand(1_234_567_890),
|
||||||
|
data: File.binread(Rails.root.join('test', 'data', 'upload', 'upload2.jpg')),
|
||||||
|
filename: 'test1.pdf',
|
||||||
|
preferences: {
|
||||||
|
content_type: 'image/jpg',
|
||||||
|
content_id: 234,
|
||||||
|
},
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(store.preferences[:resizable], true)
|
||||||
|
assert_equal(store.preferences[:content_inline], true)
|
||||||
|
assert_equal(store.preferences[:content_preview], true)
|
||||||
|
|
||||||
|
temp_file = ::Tempfile.new.path
|
||||||
|
File.binwrite(temp_file, store.content_inline)
|
||||||
|
image = Rszr::Image.load(temp_file)
|
||||||
|
assert_equal(image.width, 1800)
|
||||||
|
|
||||||
|
temp_file = ::Tempfile.new.path
|
||||||
|
File.binwrite(temp_file, store.content_preview)
|
||||||
|
image = Rszr::Image.load(temp_file)
|
||||||
|
assert_equal(image.width, 200)
|
||||||
|
|
||||||
|
# possible (preview only)
|
||||||
|
store = Store.add(
|
||||||
|
object: 'SomeObject4',
|
||||||
|
o_id: rand(1_234_567_890),
|
||||||
|
data: File.binread(Rails.root.join('test', 'data', 'image', '1000x1000.png')),
|
||||||
|
filename: 'test1.png',
|
||||||
|
preferences: {
|
||||||
|
content_type: 'image/png',
|
||||||
|
content_id: 234,
|
||||||
|
},
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(store.preferences[:resizable], true)
|
||||||
|
assert_nil(store.preferences[:content_inline])
|
||||||
|
assert_equal(store.preferences[:content_preview], true)
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_inline
|
||||||
|
end
|
||||||
|
|
||||||
|
temp_file = ::Tempfile.new.path
|
||||||
|
File.binwrite(temp_file, store.content_preview)
|
||||||
|
image = Rszr::Image.load(temp_file)
|
||||||
|
assert_equal(image.width, 200)
|
||||||
|
|
||||||
|
# possible (now preview or inline needed)
|
||||||
|
store = Store.add(
|
||||||
|
object: 'SomeObject5',
|
||||||
|
o_id: rand(1_234_567_890),
|
||||||
|
data: File.binread(Rails.root.join('test', 'data', 'image', '1x1.png')),
|
||||||
|
filename: 'test1.png',
|
||||||
|
preferences: {
|
||||||
|
content_type: 'image/png',
|
||||||
|
content_id: 234,
|
||||||
|
},
|
||||||
|
created_by_id: 1,
|
||||||
|
)
|
||||||
|
assert_equal(store.preferences[:resizable], true)
|
||||||
|
assert_nil(store.preferences[:content_inline])
|
||||||
|
assert_nil(store.preferences[:content_preview])
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_inline
|
||||||
|
end
|
||||||
|
assert_raises(RuntimeError) do
|
||||||
|
store.content_preview
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in a new issue